@fluid-internal/mocha-test-setup 2.11.0 → 2.13.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @fluid-internal/mocha-test-setup
2
2
 
3
+ ## 2.13.0
4
+
5
+ Dependency updates only.
6
+
7
+ ## 2.12.0
8
+
9
+ Dependency updates only.
10
+
3
11
  ## 2.11.0
4
12
 
5
13
  Dependency updates only.
@@ -1 +1 @@
1
- {"version":3,"file":"mochaHooks.d.ts","sourceRoot":"","sources":["../src/mochaHooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAgEH;;GAEG;AACH,eAAO,MAAM,UAAU;;qBAoBL,MAAM,OAAO;oBAoBd,MAAM,OAAO;CAuB7B,CAAC"}
1
+ {"version":3,"file":"mochaHooks.d.ts","sourceRoot":"","sources":["../src/mochaHooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAoFH;;GAEG;AACH,eAAO,MAAM,UAAU;;qBAiBL,MAAM,OAAO;oBAed,MAAM,OAAO;CAqB7B,CAAC"}
@@ -30,11 +30,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
30
30
  exports.mochaHooks = void 0;
31
31
  const mochaModule = __importStar(require("mocha"));
32
32
  const packageVersion_js_1 = require("./packageVersion.js");
33
- // this will enabling capturing the full stack for errors
34
- // since this is test capturing the full stack is worth it
35
- // in non-test environment we need to be more cautious
36
- // as this will incur a perf impact when errors are
37
- // thrown and will take more storage in any logging sink
33
+ // This will enable capturing the full stack for errors.
34
+ // Since this package is only used when we run tests, capturing the full stack is worth it.
35
+ // In non-test environments we need to be more cautious as this will incur a perf impact when errors are
36
+ // thrown and will take more storage in any logging sink.
38
37
  // https://v8.dev/docs/stack-trace-api
39
38
  Error.stackTraceLimit = Number.POSITIVE_INFINITY;
40
39
  const testVariant = process.env.FLUID_TEST_VARIANT;
@@ -42,22 +41,25 @@ const propsDict = process.env.FLUID_LOGGER_PROPS != null
42
41
  ? JSON.parse(process.env.FLUID_LOGGER_PROPS)
43
42
  : undefined;
44
43
  const _global = global;
45
- class TestLogger {
44
+ /**
45
+ * A logger that adds context about the current test run to all events logged through it, like the test variant being
46
+ * run (odsp, r11s, etc) and the name of the current test (while in the context of a particular test running).
47
+ */
48
+ class FluidTestRunLogger {
46
49
  send(event) {
47
50
  // TODO: Remove when issue #7061 is resolved.
48
51
  // Don't log this event as we generate too much.
49
52
  if (event.eventName === "fluid:telemetry:RouterliciousDriver:readBlob_end") {
50
53
  return;
51
54
  }
52
- // The test logger is currently instantiated once and for each event triggered between begin and
53
- // end of a test, in case the testName is undefined, we will use the currentTestName.
54
- event.testName = this.testName ?? currentTestName;
55
+ if (this.currentTestName !== undefined) {
56
+ event.testName = this.currentTestName;
57
+ }
55
58
  event.testVariant = testVariant;
56
- event.hostName = packageVersion_js_1.pkgName;
57
59
  this.parentLogger.send({
58
60
  ...event,
59
- // If there's an override for the hostName in FLUID_LOGGER_PROPS,
60
- // display that in the telemetry instead of the package name.
61
+ // Setting hostname to pkgName is the behavior we had for a long time, so keeping it just in case.
62
+ // But prefer a value set through FLUID_LOGGER_PROPS if it exists.
61
63
  hostName: propsDict?.hostName ?? packageVersion_js_1.pkgName,
62
64
  details: JSON.stringify(propsDict),
63
65
  });
@@ -65,84 +67,94 @@ class TestLogger {
65
67
  async flush() {
66
68
  return this.parentLogger.flush();
67
69
  }
68
- constructor(parentLogger, testName) {
70
+ constructor(parentLogger) {
69
71
  this.parentLogger = parentLogger;
70
- this.testName = testName;
72
+ }
73
+ /**
74
+ * Sets the test that is currently running.
75
+ * The test name will be included in all events logged through the logger until {@link clearCurrentTest} is called.
76
+ * @param testName - The name of the test that is currently running.
77
+ */
78
+ setCurrentTest(testName) {
79
+ this.currentTestName = testName;
80
+ }
81
+ /**
82
+ * Clears the test that is currently running.
83
+ * Must be called after a given test has completed so that events logged outside the context of a test
84
+ * don't include the name of the last test that ran.
85
+ */
86
+ clearCurrentTest() {
87
+ this.currentTestName = undefined;
71
88
  }
72
89
  }
73
90
  const nullLogger = {
74
91
  send: () => { },
75
92
  flush: async () => { },
76
93
  };
94
+ // Keep references to the original console functions so we can restore them after each test.
77
95
  const log = console.log;
78
96
  const error = console.log;
79
97
  const warn = console.warn;
80
- let currentTestLogger;
81
- let currentTestName;
82
- let originalLogger;
98
+ let testLogger;
83
99
  /**
84
100
  * @internal
85
101
  */
86
102
  exports.mochaHooks = {
87
103
  beforeAll() {
88
- originalLogger = _global.getTestLogger?.() ?? nullLogger;
89
- _global.getTestLogger = () => {
90
- // If a current test logger exists, use that. Otherwise, create a new one. This should become the
91
- // current test logger if this function is running in a context which understands the current test.
92
- // Otherwise, just return the created TestLogger. (This happens e.g. if someone calls `getTestLogger`
93
- // in a `before` or `after` hook, due to the order in which mocha hooks run)
94
- if (currentTestLogger !== undefined) {
95
- return currentTestLogger;
96
- }
97
- const testLogger = new TestLogger(originalLogger, currentTestName);
98
- if (currentTestName !== undefined) {
99
- currentTestLogger = testLogger;
100
- }
101
- return testLogger;
102
- };
104
+ // Code in our tests will call the global `getTestLogger` function to get a logger to use.
105
+ // First we call the version of that function that was (potentially) injected dynamicaly to get the logger that it
106
+ // provides and wrap it with a more intelligent logger which adds test-run-related context to all events logged
107
+ // through it. See the documentation on `FluidTestRunLogger` for details.
108
+ const originalLogger = _global.getTestLogger?.() ?? nullLogger;
109
+ testLogger = new FluidTestRunLogger(originalLogger);
110
+ // Then we redefine `getTestLogger` so it returns the wrapper logger.
111
+ // NOTE: if we have other global mocha hooks defined somewhere, they include a `beforeEach` hook, and the module in
112
+ // which they are defined gets loaded before this one, then if that `beforeEach` hook calls `getTestLogger` the logger
113
+ // it will get will still have a lot of the test-run-related context, but not the name of the current test, because
114
+ // it will run before the `beforeEach` hook in this file which sets that.
115
+ _global.getTestLogger = () => testLogger;
103
116
  },
104
117
  beforeEach() {
105
- // Suppress console.log if not verbose mode
118
+ // If not in verbose mode, suppress console output while the current test runs.
106
119
  if (process.env.FLUID_TEST_VERBOSE === undefined) {
107
120
  console.log = () => { };
108
121
  console.error = () => { };
109
122
  console.warn = () => { };
110
123
  }
111
- // save the test name can and clear the previous logger (if afterEach didn't get ran and it got left behind)
112
- currentTestName = this.currentTest?.fullTitle();
113
- currentTestLogger = undefined;
114
- // send event on test start
115
- originalLogger.send({
124
+ ensureTestRunLoggerIsInitialized(testLogger);
125
+ testLogger.setCurrentTest(this.currentTest?.fullTitle() ?? "");
126
+ testLogger.send({
116
127
  category: "generic",
117
128
  eventName: "fluid:telemetry:Test_start",
118
- testName: currentTestName,
119
- testVariant,
120
- hostName: packageVersion_js_1.pkgName,
121
129
  });
122
130
  },
123
131
  afterEach() {
124
- // send event on test end
125
- originalLogger.send({
132
+ ensureTestRunLoggerIsInitialized(testLogger);
133
+ testLogger.send({
126
134
  category: "generic",
127
135
  eventName: "fluid:telemetry:Test_end",
128
- testName: currentTestName,
129
136
  state: this.currentTest?.state,
130
137
  duration: this.currentTest?.duration,
131
138
  timedOut: this.currentTest?.timedOut,
132
- testVariant,
133
- hostName: packageVersion_js_1.pkgName,
134
139
  error: this.currentTest?.err?.message,
135
140
  stack: this.currentTest?.err?.stack,
136
141
  });
142
+ // Restore console output now that the current test is done running.
137
143
  console.log = log;
138
144
  console.error = error;
139
145
  console.warn = warn;
140
- // clear the test logger and test name after each test
141
- currentTestLogger = undefined;
142
- currentTestName = undefined;
146
+ // Clear the current test from the logger. Important so if anything calls `getTestLogger` outside the context of a
147
+ // test (e.g. during a `before` or `after` hook), it doesn't log events with the name of the last test that ran.
148
+ testLogger.clearCurrentTest();
143
149
  },
144
150
  };
145
151
  globalThis.getMochaModule = () => {
146
152
  return mochaModule;
147
153
  };
154
+ function ensureTestRunLoggerIsInitialized(loggerToTest) {
155
+ if (loggerToTest instanceof FluidTestRunLogger) {
156
+ return true;
157
+ }
158
+ throw new Error("Test run logger is not initialized");
159
+ }
148
160
  //# sourceMappingURL=mochaHooks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mochaHooks.js","sourceRoot":"","sources":["../src/mochaHooks.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 type { ITelemetryBufferedLogger } from \"@fluid-internal/test-driver-definitions\";\nimport type { ITelemetryBaseEvent } from \"@fluidframework/core-interfaces\";\nimport * as mochaModule from \"mocha\";\n\nimport { pkgName } from \"./packageVersion.js\";\n\n// this will enabling capturing the full stack for errors\n// since this is test capturing the full stack is worth it\n// in non-test environment we need to be more cautious\n// as this will incur a perf impact when errors are\n// thrown and will take more storage in any logging sink\n// https://v8.dev/docs/stack-trace-api\nError.stackTraceLimit = Number.POSITIVE_INFINITY;\n\nconst testVariant = process.env.FLUID_TEST_VARIANT;\nconst propsDict =\n\tprocess.env.FLUID_LOGGER_PROPS != null\n\t\t? JSON.parse(process.env.FLUID_LOGGER_PROPS)\n\t\t: undefined;\n\nconst _global: any = global;\nclass TestLogger implements ITelemetryBufferedLogger {\n\tsend(event: ITelemetryBaseEvent) {\n\t\t// TODO: Remove when issue #7061 is resolved.\n\t\t// Don't log this event as we generate too much.\n\t\tif (event.eventName === \"fluid:telemetry:RouterliciousDriver:readBlob_end\") {\n\t\t\treturn;\n\t\t}\n\n\t\t// The test logger is currently instantiated once and for each event triggered between begin and\n\t\t// end of a test, in case the testName is undefined, we will use the currentTestName.\n\t\tevent.testName = this.testName ?? currentTestName;\n\t\tevent.testVariant = testVariant;\n\t\tevent.hostName = pkgName;\n\t\tthis.parentLogger.send({\n\t\t\t...event,\n\t\t\t// If there's an override for the hostName in FLUID_LOGGER_PROPS,\n\t\t\t// display that in the telemetry instead of the package name.\n\t\t\thostName: propsDict?.hostName ?? pkgName,\n\t\t\tdetails: JSON.stringify(propsDict),\n\t\t});\n\t}\n\tasync flush() {\n\t\treturn this.parentLogger.flush();\n\t}\n\tconstructor(\n\t\tprivate readonly parentLogger: ITelemetryBufferedLogger,\n\t\tprivate readonly testName: string | undefined,\n\t) {}\n}\nconst nullLogger: ITelemetryBufferedLogger = {\n\tsend: () => {},\n\tflush: async () => {},\n};\n\nconst log = console.log;\nconst error = console.log;\nconst warn = console.warn;\nlet currentTestLogger: ITelemetryBufferedLogger | undefined;\nlet currentTestName: string | undefined;\nlet originalLogger: ITelemetryBufferedLogger;\n\n/**\n * @internal\n */\nexport const mochaHooks = {\n\tbeforeAll() {\n\t\toriginalLogger = _global.getTestLogger?.() ?? nullLogger;\n\t\t_global.getTestLogger = () => {\n\t\t\t// If a current test logger exists, use that. Otherwise, create a new one. This should become the\n\t\t\t// current test logger if this function is running in a context which understands the current test.\n\t\t\t// Otherwise, just return the created TestLogger. (This happens e.g. if someone calls `getTestLogger`\n\t\t\t// in a `before` or `after` hook, due to the order in which mocha hooks run)\n\t\t\tif (currentTestLogger !== undefined) {\n\t\t\t\treturn currentTestLogger;\n\t\t\t}\n\n\t\t\tconst testLogger = new TestLogger(originalLogger, currentTestName);\n\t\t\tif (currentTestName !== undefined) {\n\t\t\t\tcurrentTestLogger = testLogger;\n\t\t\t}\n\n\t\t\treturn testLogger;\n\t\t};\n\t},\n\tbeforeEach(this: Mocha.Context) {\n\t\t// Suppress console.log if not verbose mode\n\t\tif (process.env.FLUID_TEST_VERBOSE === undefined) {\n\t\t\tconsole.log = () => {};\n\t\t\tconsole.error = () => {};\n\t\t\tconsole.warn = () => {};\n\t\t}\n\t\t// save the test name can and clear the previous logger (if afterEach didn't get ran and it got left behind)\n\t\tcurrentTestName = this.currentTest?.fullTitle();\n\t\tcurrentTestLogger = undefined;\n\n\t\t// send event on test start\n\t\toriginalLogger.send({\n\t\t\tcategory: \"generic\",\n\t\t\teventName: \"fluid:telemetry:Test_start\",\n\t\t\ttestName: currentTestName,\n\t\t\ttestVariant,\n\t\t\thostName: pkgName,\n\t\t});\n\t},\n\tafterEach(this: Mocha.Context) {\n\t\t// send event on test end\n\t\toriginalLogger.send({\n\t\t\tcategory: \"generic\",\n\t\t\teventName: \"fluid:telemetry:Test_end\",\n\t\t\ttestName: currentTestName,\n\t\t\tstate: this.currentTest?.state,\n\t\t\tduration: this.currentTest?.duration,\n\t\t\ttimedOut: this.currentTest?.timedOut,\n\t\t\ttestVariant,\n\t\t\thostName: pkgName,\n\t\t\terror: this.currentTest?.err?.message,\n\t\t\tstack: this.currentTest?.err?.stack,\n\t\t});\n\n\t\tconsole.log = log;\n\t\tconsole.error = error;\n\t\tconsole.warn = warn;\n\n\t\t// clear the test logger and test name after each test\n\t\tcurrentTestLogger = undefined;\n\t\tcurrentTestName = undefined;\n\t},\n};\n\nglobalThis.getMochaModule = () => {\n\treturn mochaModule;\n};\n"]}
1
+ {"version":3,"file":"mochaHooks.js","sourceRoot":"","sources":["../src/mochaHooks.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 type { ITelemetryBufferedLogger } from \"@fluid-internal/test-driver-definitions\";\nimport type { ITelemetryBaseEvent } from \"@fluidframework/core-interfaces\";\nimport * as mochaModule from \"mocha\";\n\nimport { pkgName } from \"./packageVersion.js\";\n\n// This will enable capturing the full stack for errors.\n// Since this package is only used when we run tests, capturing the full stack is worth it.\n// In non-test environments we need to be more cautious as this will incur a perf impact when errors are\n// thrown and will take more storage in any logging sink.\n// https://v8.dev/docs/stack-trace-api\nError.stackTraceLimit = Number.POSITIVE_INFINITY;\n\nconst testVariant = process.env.FLUID_TEST_VARIANT;\nconst propsDict =\n\tprocess.env.FLUID_LOGGER_PROPS != null\n\t\t? JSON.parse(process.env.FLUID_LOGGER_PROPS)\n\t\t: undefined;\n\nconst _global: any = global;\n\n/**\n * A logger that adds context about the current test run to all events logged through it, like the test variant being\n * run (odsp, r11s, etc) and the name of the current test (while in the context of a particular test running).\n */\nclass FluidTestRunLogger implements ITelemetryBufferedLogger {\n\tprivate currentTestName: string | undefined;\n\n\tsend(event: ITelemetryBaseEvent) {\n\t\t// TODO: Remove when issue #7061 is resolved.\n\t\t// Don't log this event as we generate too much.\n\t\tif (event.eventName === \"fluid:telemetry:RouterliciousDriver:readBlob_end\") {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.currentTestName !== undefined) {\n\t\t\tevent.testName = this.currentTestName;\n\t\t}\n\t\tevent.testVariant = testVariant;\n\t\tthis.parentLogger.send({\n\t\t\t...event,\n\t\t\t// Setting hostname to pkgName is the behavior we had for a long time, so keeping it just in case.\n\t\t\t// But prefer a value set through FLUID_LOGGER_PROPS if it exists.\n\t\t\thostName: propsDict?.hostName ?? pkgName,\n\t\t\tdetails: JSON.stringify(propsDict),\n\t\t});\n\t}\n\tasync flush() {\n\t\treturn this.parentLogger.flush();\n\t}\n\tconstructor(private readonly parentLogger: ITelemetryBufferedLogger) {}\n\n\t/**\n\t * Sets the test that is currently running.\n\t * The test name will be included in all events logged through the logger until {@link clearCurrentTest} is called.\n\t * @param testName - The name of the test that is currently running.\n\t */\n\tpublic setCurrentTest(testName: string) {\n\t\tthis.currentTestName = testName;\n\t}\n\n\t/**\n\t * Clears the test that is currently running.\n\t * Must be called after a given test has completed so that events logged outside the context of a test\n\t * don't include the name of the last test that ran.\n\t */\n\tpublic clearCurrentTest() {\n\t\tthis.currentTestName = undefined;\n\t}\n}\nconst nullLogger: ITelemetryBufferedLogger = {\n\tsend: () => {},\n\tflush: async () => {},\n};\n\n// Keep references to the original console functions so we can restore them after each test.\nconst log = console.log;\nconst error = console.log;\nconst warn = console.warn;\n\nlet testLogger: FluidTestRunLogger;\n\n/**\n * @internal\n */\nexport const mochaHooks = {\n\tbeforeAll() {\n\t\t// Code in our tests will call the global `getTestLogger` function to get a logger to use.\n\n\t\t// First we call the version of that function that was (potentially) injected dynamicaly to get the logger that it\n\t\t// provides and wrap it with a more intelligent logger which adds test-run-related context to all events logged\n\t\t// through it. See the documentation on `FluidTestRunLogger` for details.\n\t\tconst originalLogger = _global.getTestLogger?.() ?? nullLogger;\n\t\ttestLogger = new FluidTestRunLogger(originalLogger);\n\n\t\t// Then we redefine `getTestLogger` so it returns the wrapper logger.\n\t\t// NOTE: if we have other global mocha hooks defined somewhere, they include a `beforeEach` hook, and the module in\n\t\t// which they are defined gets loaded before this one, then if that `beforeEach` hook calls `getTestLogger` the logger\n\t\t// it will get will still have a lot of the test-run-related context, but not the name of the current test, because\n\t\t// it will run before the `beforeEach` hook in this file which sets that.\n\t\t_global.getTestLogger = () => testLogger;\n\t},\n\tbeforeEach(this: Mocha.Context) {\n\t\t// If not in verbose mode, suppress console output while the current test runs.\n\t\tif (process.env.FLUID_TEST_VERBOSE === undefined) {\n\t\t\tconsole.log = () => {};\n\t\t\tconsole.error = () => {};\n\t\t\tconsole.warn = () => {};\n\t\t}\n\n\t\tensureTestRunLoggerIsInitialized(testLogger);\n\t\ttestLogger.setCurrentTest(this.currentTest?.fullTitle() ?? \"\");\n\t\ttestLogger.send({\n\t\t\tcategory: \"generic\",\n\t\t\teventName: \"fluid:telemetry:Test_start\",\n\t\t});\n\t},\n\tafterEach(this: Mocha.Context) {\n\t\tensureTestRunLoggerIsInitialized(testLogger);\n\t\ttestLogger.send({\n\t\t\tcategory: \"generic\",\n\t\t\teventName: \"fluid:telemetry:Test_end\",\n\t\t\tstate: this.currentTest?.state,\n\t\t\tduration: this.currentTest?.duration,\n\t\t\ttimedOut: this.currentTest?.timedOut,\n\t\t\terror: this.currentTest?.err?.message,\n\t\t\tstack: this.currentTest?.err?.stack,\n\t\t});\n\n\t\t// Restore console output now that the current test is done running.\n\t\tconsole.log = log;\n\t\tconsole.error = error;\n\t\tconsole.warn = warn;\n\n\t\t// Clear the current test from the logger. Important so if anything calls `getTestLogger` outside the context of a\n\t\t// test (e.g. during a `before` or `after` hook), it doesn't log events with the name of the last test that ran.\n\t\ttestLogger.clearCurrentTest();\n\t},\n};\n\nglobalThis.getMochaModule = () => {\n\treturn mochaModule;\n};\n\nfunction ensureTestRunLoggerIsInitialized(\n\tloggerToTest: FluidTestRunLogger | undefined,\n): loggerToTest is FluidTestRunLogger {\n\tif (loggerToTest instanceof FluidTestRunLogger) {\n\t\treturn true;\n\t}\n\tthrow new Error(\"Test run logger is not initialized\");\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 = "2.11.0";
8
+ export declare const pkgVersion = "2.13.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluid-internal/mocha-test-setup";
11
- exports.pkgVersion = "2.11.0";
11
+ exports.pkgVersion = "2.13.0";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,kCAAkC,CAAC;AAC7C,QAAA,UAAU,GAAG,QAAQ,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 = \"2.11.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,kCAAkC,CAAC;AAC7C,QAAA,UAAU,GAAG,QAAQ,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 = \"2.13.0\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"mochaHooks.d.ts","sourceRoot":"","sources":["../src/mochaHooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAgEH;;GAEG;AACH,eAAO,MAAM,UAAU;;qBAoBL,MAAM,OAAO;oBAoBd,MAAM,OAAO;CAuB7B,CAAC"}
1
+ {"version":3,"file":"mochaHooks.d.ts","sourceRoot":"","sources":["../src/mochaHooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAoFH;;GAEG;AACH,eAAO,MAAM,UAAU;;qBAiBL,MAAM,OAAO;oBAed,MAAM,OAAO;CAqB7B,CAAC"}
package/lib/mochaHooks.js CHANGED
@@ -4,11 +4,10 @@
4
4
  */
5
5
  import * as mochaModule from "mocha";
6
6
  import { pkgName } from "./packageVersion.js";
7
- // this will enabling capturing the full stack for errors
8
- // since this is test capturing the full stack is worth it
9
- // in non-test environment we need to be more cautious
10
- // as this will incur a perf impact when errors are
11
- // thrown and will take more storage in any logging sink
7
+ // This will enable capturing the full stack for errors.
8
+ // Since this package is only used when we run tests, capturing the full stack is worth it.
9
+ // In non-test environments we need to be more cautious as this will incur a perf impact when errors are
10
+ // thrown and will take more storage in any logging sink.
12
11
  // https://v8.dev/docs/stack-trace-api
13
12
  Error.stackTraceLimit = Number.POSITIVE_INFINITY;
14
13
  const testVariant = process.env.FLUID_TEST_VARIANT;
@@ -16,22 +15,25 @@ const propsDict = process.env.FLUID_LOGGER_PROPS != null
16
15
  ? JSON.parse(process.env.FLUID_LOGGER_PROPS)
17
16
  : undefined;
18
17
  const _global = global;
19
- class TestLogger {
18
+ /**
19
+ * A logger that adds context about the current test run to all events logged through it, like the test variant being
20
+ * run (odsp, r11s, etc) and the name of the current test (while in the context of a particular test running).
21
+ */
22
+ class FluidTestRunLogger {
20
23
  send(event) {
21
24
  // TODO: Remove when issue #7061 is resolved.
22
25
  // Don't log this event as we generate too much.
23
26
  if (event.eventName === "fluid:telemetry:RouterliciousDriver:readBlob_end") {
24
27
  return;
25
28
  }
26
- // The test logger is currently instantiated once and for each event triggered between begin and
27
- // end of a test, in case the testName is undefined, we will use the currentTestName.
28
- event.testName = this.testName ?? currentTestName;
29
+ if (this.currentTestName !== undefined) {
30
+ event.testName = this.currentTestName;
31
+ }
29
32
  event.testVariant = testVariant;
30
- event.hostName = pkgName;
31
33
  this.parentLogger.send({
32
34
  ...event,
33
- // If there's an override for the hostName in FLUID_LOGGER_PROPS,
34
- // display that in the telemetry instead of the package name.
35
+ // Setting hostname to pkgName is the behavior we had for a long time, so keeping it just in case.
36
+ // But prefer a value set through FLUID_LOGGER_PROPS if it exists.
35
37
  hostName: propsDict?.hostName ?? pkgName,
36
38
  details: JSON.stringify(propsDict),
37
39
  });
@@ -39,84 +41,94 @@ class TestLogger {
39
41
  async flush() {
40
42
  return this.parentLogger.flush();
41
43
  }
42
- constructor(parentLogger, testName) {
44
+ constructor(parentLogger) {
43
45
  this.parentLogger = parentLogger;
44
- this.testName = testName;
46
+ }
47
+ /**
48
+ * Sets the test that is currently running.
49
+ * The test name will be included in all events logged through the logger until {@link clearCurrentTest} is called.
50
+ * @param testName - The name of the test that is currently running.
51
+ */
52
+ setCurrentTest(testName) {
53
+ this.currentTestName = testName;
54
+ }
55
+ /**
56
+ * Clears the test that is currently running.
57
+ * Must be called after a given test has completed so that events logged outside the context of a test
58
+ * don't include the name of the last test that ran.
59
+ */
60
+ clearCurrentTest() {
61
+ this.currentTestName = undefined;
45
62
  }
46
63
  }
47
64
  const nullLogger = {
48
65
  send: () => { },
49
66
  flush: async () => { },
50
67
  };
68
+ // Keep references to the original console functions so we can restore them after each test.
51
69
  const log = console.log;
52
70
  const error = console.log;
53
71
  const warn = console.warn;
54
- let currentTestLogger;
55
- let currentTestName;
56
- let originalLogger;
72
+ let testLogger;
57
73
  /**
58
74
  * @internal
59
75
  */
60
76
  export const mochaHooks = {
61
77
  beforeAll() {
62
- originalLogger = _global.getTestLogger?.() ?? nullLogger;
63
- _global.getTestLogger = () => {
64
- // If a current test logger exists, use that. Otherwise, create a new one. This should become the
65
- // current test logger if this function is running in a context which understands the current test.
66
- // Otherwise, just return the created TestLogger. (This happens e.g. if someone calls `getTestLogger`
67
- // in a `before` or `after` hook, due to the order in which mocha hooks run)
68
- if (currentTestLogger !== undefined) {
69
- return currentTestLogger;
70
- }
71
- const testLogger = new TestLogger(originalLogger, currentTestName);
72
- if (currentTestName !== undefined) {
73
- currentTestLogger = testLogger;
74
- }
75
- return testLogger;
76
- };
78
+ // Code in our tests will call the global `getTestLogger` function to get a logger to use.
79
+ // First we call the version of that function that was (potentially) injected dynamicaly to get the logger that it
80
+ // provides and wrap it with a more intelligent logger which adds test-run-related context to all events logged
81
+ // through it. See the documentation on `FluidTestRunLogger` for details.
82
+ const originalLogger = _global.getTestLogger?.() ?? nullLogger;
83
+ testLogger = new FluidTestRunLogger(originalLogger);
84
+ // Then we redefine `getTestLogger` so it returns the wrapper logger.
85
+ // NOTE: if we have other global mocha hooks defined somewhere, they include a `beforeEach` hook, and the module in
86
+ // which they are defined gets loaded before this one, then if that `beforeEach` hook calls `getTestLogger` the logger
87
+ // it will get will still have a lot of the test-run-related context, but not the name of the current test, because
88
+ // it will run before the `beforeEach` hook in this file which sets that.
89
+ _global.getTestLogger = () => testLogger;
77
90
  },
78
91
  beforeEach() {
79
- // Suppress console.log if not verbose mode
92
+ // If not in verbose mode, suppress console output while the current test runs.
80
93
  if (process.env.FLUID_TEST_VERBOSE === undefined) {
81
94
  console.log = () => { };
82
95
  console.error = () => { };
83
96
  console.warn = () => { };
84
97
  }
85
- // save the test name can and clear the previous logger (if afterEach didn't get ran and it got left behind)
86
- currentTestName = this.currentTest?.fullTitle();
87
- currentTestLogger = undefined;
88
- // send event on test start
89
- originalLogger.send({
98
+ ensureTestRunLoggerIsInitialized(testLogger);
99
+ testLogger.setCurrentTest(this.currentTest?.fullTitle() ?? "");
100
+ testLogger.send({
90
101
  category: "generic",
91
102
  eventName: "fluid:telemetry:Test_start",
92
- testName: currentTestName,
93
- testVariant,
94
- hostName: pkgName,
95
103
  });
96
104
  },
97
105
  afterEach() {
98
- // send event on test end
99
- originalLogger.send({
106
+ ensureTestRunLoggerIsInitialized(testLogger);
107
+ testLogger.send({
100
108
  category: "generic",
101
109
  eventName: "fluid:telemetry:Test_end",
102
- testName: currentTestName,
103
110
  state: this.currentTest?.state,
104
111
  duration: this.currentTest?.duration,
105
112
  timedOut: this.currentTest?.timedOut,
106
- testVariant,
107
- hostName: pkgName,
108
113
  error: this.currentTest?.err?.message,
109
114
  stack: this.currentTest?.err?.stack,
110
115
  });
116
+ // Restore console output now that the current test is done running.
111
117
  console.log = log;
112
118
  console.error = error;
113
119
  console.warn = warn;
114
- // clear the test logger and test name after each test
115
- currentTestLogger = undefined;
116
- currentTestName = undefined;
120
+ // Clear the current test from the logger. Important so if anything calls `getTestLogger` outside the context of a
121
+ // test (e.g. during a `before` or `after` hook), it doesn't log events with the name of the last test that ran.
122
+ testLogger.clearCurrentTest();
117
123
  },
118
124
  };
119
125
  globalThis.getMochaModule = () => {
120
126
  return mochaModule;
121
127
  };
128
+ function ensureTestRunLoggerIsInitialized(loggerToTest) {
129
+ if (loggerToTest instanceof FluidTestRunLogger) {
130
+ return true;
131
+ }
132
+ throw new Error("Test run logger is not initialized");
133
+ }
122
134
  //# sourceMappingURL=mochaHooks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mochaHooks.js","sourceRoot":"","sources":["../src/mochaHooks.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 type { ITelemetryBufferedLogger } from \"@fluid-internal/test-driver-definitions\";\nimport type { ITelemetryBaseEvent } from \"@fluidframework/core-interfaces\";\nimport * as mochaModule from \"mocha\";\n\nimport { pkgName } from \"./packageVersion.js\";\n\n// this will enabling capturing the full stack for errors\n// since this is test capturing the full stack is worth it\n// in non-test environment we need to be more cautious\n// as this will incur a perf impact when errors are\n// thrown and will take more storage in any logging sink\n// https://v8.dev/docs/stack-trace-api\nError.stackTraceLimit = Number.POSITIVE_INFINITY;\n\nconst testVariant = process.env.FLUID_TEST_VARIANT;\nconst propsDict =\n\tprocess.env.FLUID_LOGGER_PROPS != null\n\t\t? JSON.parse(process.env.FLUID_LOGGER_PROPS)\n\t\t: undefined;\n\nconst _global: any = global;\nclass TestLogger implements ITelemetryBufferedLogger {\n\tsend(event: ITelemetryBaseEvent) {\n\t\t// TODO: Remove when issue #7061 is resolved.\n\t\t// Don't log this event as we generate too much.\n\t\tif (event.eventName === \"fluid:telemetry:RouterliciousDriver:readBlob_end\") {\n\t\t\treturn;\n\t\t}\n\n\t\t// The test logger is currently instantiated once and for each event triggered between begin and\n\t\t// end of a test, in case the testName is undefined, we will use the currentTestName.\n\t\tevent.testName = this.testName ?? currentTestName;\n\t\tevent.testVariant = testVariant;\n\t\tevent.hostName = pkgName;\n\t\tthis.parentLogger.send({\n\t\t\t...event,\n\t\t\t// If there's an override for the hostName in FLUID_LOGGER_PROPS,\n\t\t\t// display that in the telemetry instead of the package name.\n\t\t\thostName: propsDict?.hostName ?? pkgName,\n\t\t\tdetails: JSON.stringify(propsDict),\n\t\t});\n\t}\n\tasync flush() {\n\t\treturn this.parentLogger.flush();\n\t}\n\tconstructor(\n\t\tprivate readonly parentLogger: ITelemetryBufferedLogger,\n\t\tprivate readonly testName: string | undefined,\n\t) {}\n}\nconst nullLogger: ITelemetryBufferedLogger = {\n\tsend: () => {},\n\tflush: async () => {},\n};\n\nconst log = console.log;\nconst error = console.log;\nconst warn = console.warn;\nlet currentTestLogger: ITelemetryBufferedLogger | undefined;\nlet currentTestName: string | undefined;\nlet originalLogger: ITelemetryBufferedLogger;\n\n/**\n * @internal\n */\nexport const mochaHooks = {\n\tbeforeAll() {\n\t\toriginalLogger = _global.getTestLogger?.() ?? nullLogger;\n\t\t_global.getTestLogger = () => {\n\t\t\t// If a current test logger exists, use that. Otherwise, create a new one. This should become the\n\t\t\t// current test logger if this function is running in a context which understands the current test.\n\t\t\t// Otherwise, just return the created TestLogger. (This happens e.g. if someone calls `getTestLogger`\n\t\t\t// in a `before` or `after` hook, due to the order in which mocha hooks run)\n\t\t\tif (currentTestLogger !== undefined) {\n\t\t\t\treturn currentTestLogger;\n\t\t\t}\n\n\t\t\tconst testLogger = new TestLogger(originalLogger, currentTestName);\n\t\t\tif (currentTestName !== undefined) {\n\t\t\t\tcurrentTestLogger = testLogger;\n\t\t\t}\n\n\t\t\treturn testLogger;\n\t\t};\n\t},\n\tbeforeEach(this: Mocha.Context) {\n\t\t// Suppress console.log if not verbose mode\n\t\tif (process.env.FLUID_TEST_VERBOSE === undefined) {\n\t\t\tconsole.log = () => {};\n\t\t\tconsole.error = () => {};\n\t\t\tconsole.warn = () => {};\n\t\t}\n\t\t// save the test name can and clear the previous logger (if afterEach didn't get ran and it got left behind)\n\t\tcurrentTestName = this.currentTest?.fullTitle();\n\t\tcurrentTestLogger = undefined;\n\n\t\t// send event on test start\n\t\toriginalLogger.send({\n\t\t\tcategory: \"generic\",\n\t\t\teventName: \"fluid:telemetry:Test_start\",\n\t\t\ttestName: currentTestName,\n\t\t\ttestVariant,\n\t\t\thostName: pkgName,\n\t\t});\n\t},\n\tafterEach(this: Mocha.Context) {\n\t\t// send event on test end\n\t\toriginalLogger.send({\n\t\t\tcategory: \"generic\",\n\t\t\teventName: \"fluid:telemetry:Test_end\",\n\t\t\ttestName: currentTestName,\n\t\t\tstate: this.currentTest?.state,\n\t\t\tduration: this.currentTest?.duration,\n\t\t\ttimedOut: this.currentTest?.timedOut,\n\t\t\ttestVariant,\n\t\t\thostName: pkgName,\n\t\t\terror: this.currentTest?.err?.message,\n\t\t\tstack: this.currentTest?.err?.stack,\n\t\t});\n\n\t\tconsole.log = log;\n\t\tconsole.error = error;\n\t\tconsole.warn = warn;\n\n\t\t// clear the test logger and test name after each test\n\t\tcurrentTestLogger = undefined;\n\t\tcurrentTestName = undefined;\n\t},\n};\n\nglobalThis.getMochaModule = () => {\n\treturn mochaModule;\n};\n"]}
1
+ {"version":3,"file":"mochaHooks.js","sourceRoot":"","sources":["../src/mochaHooks.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 type { ITelemetryBufferedLogger } from \"@fluid-internal/test-driver-definitions\";\nimport type { ITelemetryBaseEvent } from \"@fluidframework/core-interfaces\";\nimport * as mochaModule from \"mocha\";\n\nimport { pkgName } from \"./packageVersion.js\";\n\n// This will enable capturing the full stack for errors.\n// Since this package is only used when we run tests, capturing the full stack is worth it.\n// In non-test environments we need to be more cautious as this will incur a perf impact when errors are\n// thrown and will take more storage in any logging sink.\n// https://v8.dev/docs/stack-trace-api\nError.stackTraceLimit = Number.POSITIVE_INFINITY;\n\nconst testVariant = process.env.FLUID_TEST_VARIANT;\nconst propsDict =\n\tprocess.env.FLUID_LOGGER_PROPS != null\n\t\t? JSON.parse(process.env.FLUID_LOGGER_PROPS)\n\t\t: undefined;\n\nconst _global: any = global;\n\n/**\n * A logger that adds context about the current test run to all events logged through it, like the test variant being\n * run (odsp, r11s, etc) and the name of the current test (while in the context of a particular test running).\n */\nclass FluidTestRunLogger implements ITelemetryBufferedLogger {\n\tprivate currentTestName: string | undefined;\n\n\tsend(event: ITelemetryBaseEvent) {\n\t\t// TODO: Remove when issue #7061 is resolved.\n\t\t// Don't log this event as we generate too much.\n\t\tif (event.eventName === \"fluid:telemetry:RouterliciousDriver:readBlob_end\") {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.currentTestName !== undefined) {\n\t\t\tevent.testName = this.currentTestName;\n\t\t}\n\t\tevent.testVariant = testVariant;\n\t\tthis.parentLogger.send({\n\t\t\t...event,\n\t\t\t// Setting hostname to pkgName is the behavior we had for a long time, so keeping it just in case.\n\t\t\t// But prefer a value set through FLUID_LOGGER_PROPS if it exists.\n\t\t\thostName: propsDict?.hostName ?? pkgName,\n\t\t\tdetails: JSON.stringify(propsDict),\n\t\t});\n\t}\n\tasync flush() {\n\t\treturn this.parentLogger.flush();\n\t}\n\tconstructor(private readonly parentLogger: ITelemetryBufferedLogger) {}\n\n\t/**\n\t * Sets the test that is currently running.\n\t * The test name will be included in all events logged through the logger until {@link clearCurrentTest} is called.\n\t * @param testName - The name of the test that is currently running.\n\t */\n\tpublic setCurrentTest(testName: string) {\n\t\tthis.currentTestName = testName;\n\t}\n\n\t/**\n\t * Clears the test that is currently running.\n\t * Must be called after a given test has completed so that events logged outside the context of a test\n\t * don't include the name of the last test that ran.\n\t */\n\tpublic clearCurrentTest() {\n\t\tthis.currentTestName = undefined;\n\t}\n}\nconst nullLogger: ITelemetryBufferedLogger = {\n\tsend: () => {},\n\tflush: async () => {},\n};\n\n// Keep references to the original console functions so we can restore them after each test.\nconst log = console.log;\nconst error = console.log;\nconst warn = console.warn;\n\nlet testLogger: FluidTestRunLogger;\n\n/**\n * @internal\n */\nexport const mochaHooks = {\n\tbeforeAll() {\n\t\t// Code in our tests will call the global `getTestLogger` function to get a logger to use.\n\n\t\t// First we call the version of that function that was (potentially) injected dynamicaly to get the logger that it\n\t\t// provides and wrap it with a more intelligent logger which adds test-run-related context to all events logged\n\t\t// through it. See the documentation on `FluidTestRunLogger` for details.\n\t\tconst originalLogger = _global.getTestLogger?.() ?? nullLogger;\n\t\ttestLogger = new FluidTestRunLogger(originalLogger);\n\n\t\t// Then we redefine `getTestLogger` so it returns the wrapper logger.\n\t\t// NOTE: if we have other global mocha hooks defined somewhere, they include a `beforeEach` hook, and the module in\n\t\t// which they are defined gets loaded before this one, then if that `beforeEach` hook calls `getTestLogger` the logger\n\t\t// it will get will still have a lot of the test-run-related context, but not the name of the current test, because\n\t\t// it will run before the `beforeEach` hook in this file which sets that.\n\t\t_global.getTestLogger = () => testLogger;\n\t},\n\tbeforeEach(this: Mocha.Context) {\n\t\t// If not in verbose mode, suppress console output while the current test runs.\n\t\tif (process.env.FLUID_TEST_VERBOSE === undefined) {\n\t\t\tconsole.log = () => {};\n\t\t\tconsole.error = () => {};\n\t\t\tconsole.warn = () => {};\n\t\t}\n\n\t\tensureTestRunLoggerIsInitialized(testLogger);\n\t\ttestLogger.setCurrentTest(this.currentTest?.fullTitle() ?? \"\");\n\t\ttestLogger.send({\n\t\t\tcategory: \"generic\",\n\t\t\teventName: \"fluid:telemetry:Test_start\",\n\t\t});\n\t},\n\tafterEach(this: Mocha.Context) {\n\t\tensureTestRunLoggerIsInitialized(testLogger);\n\t\ttestLogger.send({\n\t\t\tcategory: \"generic\",\n\t\t\teventName: \"fluid:telemetry:Test_end\",\n\t\t\tstate: this.currentTest?.state,\n\t\t\tduration: this.currentTest?.duration,\n\t\t\ttimedOut: this.currentTest?.timedOut,\n\t\t\terror: this.currentTest?.err?.message,\n\t\t\tstack: this.currentTest?.err?.stack,\n\t\t});\n\n\t\t// Restore console output now that the current test is done running.\n\t\tconsole.log = log;\n\t\tconsole.error = error;\n\t\tconsole.warn = warn;\n\n\t\t// Clear the current test from the logger. Important so if anything calls `getTestLogger` outside the context of a\n\t\t// test (e.g. during a `before` or `after` hook), it doesn't log events with the name of the last test that ran.\n\t\ttestLogger.clearCurrentTest();\n\t},\n};\n\nglobalThis.getMochaModule = () => {\n\treturn mochaModule;\n};\n\nfunction ensureTestRunLoggerIsInitialized(\n\tloggerToTest: FluidTestRunLogger | undefined,\n): loggerToTest is FluidTestRunLogger {\n\tif (loggerToTest instanceof FluidTestRunLogger) {\n\t\treturn true;\n\t}\n\tthrow new Error(\"Test run logger is not initialized\");\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 = "2.11.0";
8
+ export declare const pkgVersion = "2.13.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 = "2.11.0";
8
+ export const pkgVersion = "2.13.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,QAAQ,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 = \"2.11.0\";\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,QAAQ,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 = \"2.13.0\";\n"]}
@@ -50,11 +50,15 @@ function getFluidTestMochaConfig(packageDir, additionalRequiredModules, testRepo
50
50
  "recursive": true,
51
51
  "require": requiredModulePaths,
52
52
  "unhandled-rejections": "strict",
53
- // Allow test-only indexes to be imported. Search the FF repo for package.json files with this condition to see example usage.
54
- "node-option": "conditions=allow-ff-test-exports",
55
- // Performance tests benefit from having access to GC, and memory tests require it.
56
- // Exposing it here avoids all packages which do perf testing from having to expose it.
57
- "v8-expose-gc": true,
53
+ "node-option": [
54
+ // Allow test-only indexes to be imported. Search the FF repo for package.json files with this condition to see example usage.
55
+ "conditions=allow-ff-test-exports",
56
+ // Performance tests benefit from having access to GC, and memory tests require it.
57
+ // Exposing it here avoids all packages which do perf testing from having to expose it.
58
+ // Note that since "node-option" is explicitly set,
59
+ // these must be provided here and not via mocha's --v8-expose-gc.
60
+ "expose-gc",
61
+ ],
58
62
  };
59
63
 
60
64
  if (process.env.FLUID_TEST_TIMEOUT !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-internal/mocha-test-setup",
3
- "version": "2.11.0",
3
+ "version": "2.13.0",
4
4
  "description": "Utilities for Fluid tests",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -30,20 +30,20 @@
30
30
  "main": "dist/index.js",
31
31
  "types": "dist/index.d.ts",
32
32
  "dependencies": {
33
- "@fluid-internal/test-driver-definitions": "~2.11.0",
34
- "@fluidframework/core-interfaces": "~2.11.0",
33
+ "@fluid-internal/test-driver-definitions": "~2.13.0",
34
+ "@fluidframework/core-interfaces": "~2.13.0",
35
35
  "mocha": "^10.2.0",
36
36
  "source-map-support": "^0.5.21"
37
37
  },
38
38
  "devDependencies": {
39
- "@arethetypeswrong/cli": "^0.16.4",
39
+ "@arethetypeswrong/cli": "^0.17.1",
40
40
  "@biomejs/biome": "~1.9.3",
41
41
  "@fluid-tools/build-cli": "^0.51.0",
42
42
  "@fluidframework/build-common": "^2.0.3",
43
43
  "@fluidframework/build-tools": "^0.51.0",
44
44
  "@fluidframework/eslint-config-fluid": "^5.6.0",
45
45
  "@microsoft/api-extractor": "7.47.8",
46
- "@types/mocha": "^9.1.1",
46
+ "@types/mocha": "^10.0.10",
47
47
  "@types/node": "^18.19.0",
48
48
  "concurrently": "^8.2.1",
49
49
  "copyfiles": "^2.4.1",
package/src/mochaHooks.ts CHANGED
@@ -9,11 +9,10 @@ import * as mochaModule from "mocha";
9
9
 
10
10
  import { pkgName } from "./packageVersion.js";
11
11
 
12
- // this will enabling capturing the full stack for errors
13
- // since this is test capturing the full stack is worth it
14
- // in non-test environment we need to be more cautious
15
- // as this will incur a perf impact when errors are
16
- // thrown and will take more storage in any logging sink
12
+ // This will enable capturing the full stack for errors.
13
+ // Since this package is only used when we run tests, capturing the full stack is worth it.
14
+ // In non-test environments we need to be more cautious as this will incur a perf impact when errors are
15
+ // thrown and will take more storage in any logging sink.
17
16
  // https://v8.dev/docs/stack-trace-api
18
17
  Error.stackTraceLimit = Number.POSITIVE_INFINITY;
19
18
 
@@ -24,7 +23,14 @@ const propsDict =
24
23
  : undefined;
25
24
 
26
25
  const _global: any = global;
27
- class TestLogger implements ITelemetryBufferedLogger {
26
+
27
+ /**
28
+ * A logger that adds context about the current test run to all events logged through it, like the test variant being
29
+ * run (odsp, r11s, etc) and the name of the current test (while in the context of a particular test running).
30
+ */
31
+ class FluidTestRunLogger implements ITelemetryBufferedLogger {
32
+ private currentTestName: string | undefined;
33
+
28
34
  send(event: ITelemetryBaseEvent) {
29
35
  // TODO: Remove when issue #7061 is resolved.
30
36
  // Don't log this event as we generate too much.
@@ -32,15 +38,14 @@ class TestLogger implements ITelemetryBufferedLogger {
32
38
  return;
33
39
  }
34
40
 
35
- // The test logger is currently instantiated once and for each event triggered between begin and
36
- // end of a test, in case the testName is undefined, we will use the currentTestName.
37
- event.testName = this.testName ?? currentTestName;
41
+ if (this.currentTestName !== undefined) {
42
+ event.testName = this.currentTestName;
43
+ }
38
44
  event.testVariant = testVariant;
39
- event.hostName = pkgName;
40
45
  this.parentLogger.send({
41
46
  ...event,
42
- // If there's an override for the hostName in FLUID_LOGGER_PROPS,
43
- // display that in the telemetry instead of the package name.
47
+ // Setting hostname to pkgName is the behavior we had for a long time, so keeping it just in case.
48
+ // But prefer a value set through FLUID_LOGGER_PROPS if it exists.
44
49
  hostName: propsDict?.hostName ?? pkgName,
45
50
  details: JSON.stringify(propsDict),
46
51
  });
@@ -48,91 +53,105 @@ class TestLogger implements ITelemetryBufferedLogger {
48
53
  async flush() {
49
54
  return this.parentLogger.flush();
50
55
  }
51
- constructor(
52
- private readonly parentLogger: ITelemetryBufferedLogger,
53
- private readonly testName: string | undefined,
54
- ) {}
56
+ constructor(private readonly parentLogger: ITelemetryBufferedLogger) {}
57
+
58
+ /**
59
+ * Sets the test that is currently running.
60
+ * The test name will be included in all events logged through the logger until {@link clearCurrentTest} is called.
61
+ * @param testName - The name of the test that is currently running.
62
+ */
63
+ public setCurrentTest(testName: string) {
64
+ this.currentTestName = testName;
65
+ }
66
+
67
+ /**
68
+ * Clears the test that is currently running.
69
+ * Must be called after a given test has completed so that events logged outside the context of a test
70
+ * don't include the name of the last test that ran.
71
+ */
72
+ public clearCurrentTest() {
73
+ this.currentTestName = undefined;
74
+ }
55
75
  }
56
76
  const nullLogger: ITelemetryBufferedLogger = {
57
77
  send: () => {},
58
78
  flush: async () => {},
59
79
  };
60
80
 
81
+ // Keep references to the original console functions so we can restore them after each test.
61
82
  const log = console.log;
62
83
  const error = console.log;
63
84
  const warn = console.warn;
64
- let currentTestLogger: ITelemetryBufferedLogger | undefined;
65
- let currentTestName: string | undefined;
66
- let originalLogger: ITelemetryBufferedLogger;
85
+
86
+ let testLogger: FluidTestRunLogger;
67
87
 
68
88
  /**
69
89
  * @internal
70
90
  */
71
91
  export const mochaHooks = {
72
92
  beforeAll() {
73
- originalLogger = _global.getTestLogger?.() ?? nullLogger;
74
- _global.getTestLogger = () => {
75
- // If a current test logger exists, use that. Otherwise, create a new one. This should become the
76
- // current test logger if this function is running in a context which understands the current test.
77
- // Otherwise, just return the created TestLogger. (This happens e.g. if someone calls `getTestLogger`
78
- // in a `before` or `after` hook, due to the order in which mocha hooks run)
79
- if (currentTestLogger !== undefined) {
80
- return currentTestLogger;
81
- }
82
-
83
- const testLogger = new TestLogger(originalLogger, currentTestName);
84
- if (currentTestName !== undefined) {
85
- currentTestLogger = testLogger;
86
- }
87
-
88
- return testLogger;
89
- };
93
+ // Code in our tests will call the global `getTestLogger` function to get a logger to use.
94
+
95
+ // First we call the version of that function that was (potentially) injected dynamicaly to get the logger that it
96
+ // provides and wrap it with a more intelligent logger which adds test-run-related context to all events logged
97
+ // through it. See the documentation on `FluidTestRunLogger` for details.
98
+ const originalLogger = _global.getTestLogger?.() ?? nullLogger;
99
+ testLogger = new FluidTestRunLogger(originalLogger);
100
+
101
+ // Then we redefine `getTestLogger` so it returns the wrapper logger.
102
+ // NOTE: if we have other global mocha hooks defined somewhere, they include a `beforeEach` hook, and the module in
103
+ // which they are defined gets loaded before this one, then if that `beforeEach` hook calls `getTestLogger` the logger
104
+ // it will get will still have a lot of the test-run-related context, but not the name of the current test, because
105
+ // it will run before the `beforeEach` hook in this file which sets that.
106
+ _global.getTestLogger = () => testLogger;
90
107
  },
91
108
  beforeEach(this: Mocha.Context) {
92
- // Suppress console.log if not verbose mode
109
+ // If not in verbose mode, suppress console output while the current test runs.
93
110
  if (process.env.FLUID_TEST_VERBOSE === undefined) {
94
111
  console.log = () => {};
95
112
  console.error = () => {};
96
113
  console.warn = () => {};
97
114
  }
98
- // save the test name can and clear the previous logger (if afterEach didn't get ran and it got left behind)
99
- currentTestName = this.currentTest?.fullTitle();
100
- currentTestLogger = undefined;
101
115
 
102
- // send event on test start
103
- originalLogger.send({
116
+ ensureTestRunLoggerIsInitialized(testLogger);
117
+ testLogger.setCurrentTest(this.currentTest?.fullTitle() ?? "");
118
+ testLogger.send({
104
119
  category: "generic",
105
120
  eventName: "fluid:telemetry:Test_start",
106
- testName: currentTestName,
107
- testVariant,
108
- hostName: pkgName,
109
121
  });
110
122
  },
111
123
  afterEach(this: Mocha.Context) {
112
- // send event on test end
113
- originalLogger.send({
124
+ ensureTestRunLoggerIsInitialized(testLogger);
125
+ testLogger.send({
114
126
  category: "generic",
115
127
  eventName: "fluid:telemetry:Test_end",
116
- testName: currentTestName,
117
128
  state: this.currentTest?.state,
118
129
  duration: this.currentTest?.duration,
119
130
  timedOut: this.currentTest?.timedOut,
120
- testVariant,
121
- hostName: pkgName,
122
131
  error: this.currentTest?.err?.message,
123
132
  stack: this.currentTest?.err?.stack,
124
133
  });
125
134
 
135
+ // Restore console output now that the current test is done running.
126
136
  console.log = log;
127
137
  console.error = error;
128
138
  console.warn = warn;
129
139
 
130
- // clear the test logger and test name after each test
131
- currentTestLogger = undefined;
132
- currentTestName = undefined;
140
+ // Clear the current test from the logger. Important so if anything calls `getTestLogger` outside the context of a
141
+ // test (e.g. during a `before` or `after` hook), it doesn't log events with the name of the last test that ran.
142
+ testLogger.clearCurrentTest();
133
143
  },
134
144
  };
135
145
 
136
146
  globalThis.getMochaModule = () => {
137
147
  return mochaModule;
138
148
  };
149
+
150
+ function ensureTestRunLoggerIsInitialized(
151
+ loggerToTest: FluidTestRunLogger | undefined,
152
+ ): loggerToTest is FluidTestRunLogger {
153
+ if (loggerToTest instanceof FluidTestRunLogger) {
154
+ return true;
155
+ }
156
+ throw new Error("Test run logger is not initialized");
157
+ }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluid-internal/mocha-test-setup";
9
- export const pkgVersion = "2.11.0";
9
+ export const pkgVersion = "2.13.0";