@fluidframework/test-utils 2.90.0 → 2.91.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +2 -0
  3. package/dist/eventAndErrorLogger.d.ts +51 -0
  4. package/dist/eventAndErrorLogger.d.ts.map +1 -0
  5. package/dist/eventAndErrorLogger.js +116 -0
  6. package/dist/eventAndErrorLogger.js.map +1 -0
  7. package/dist/index.d.ts +7 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +14 -5
  10. package/dist/index.js.map +1 -1
  11. package/dist/packageVersion.d.ts +1 -1
  12. package/dist/packageVersion.js +1 -1
  13. package/dist/packageVersion.js.map +1 -1
  14. package/dist/testObjectProvider.d.ts +4 -46
  15. package/dist/testObjectProvider.d.ts.map +1 -1
  16. package/dist/testObjectProvider.js +6 -113
  17. package/dist/testObjectProvider.js.map +1 -1
  18. package/dist/timeoutUtils.d.ts.map +1 -1
  19. package/dist/timeoutUtils.js +2 -0
  20. package/dist/timeoutUtils.js.map +1 -1
  21. package/lib/eventAndErrorLogger.d.ts +51 -0
  22. package/lib/eventAndErrorLogger.d.ts.map +1 -0
  23. package/lib/eventAndErrorLogger.js +111 -0
  24. package/lib/eventAndErrorLogger.js.map +1 -0
  25. package/lib/index.d.ts +7 -3
  26. package/lib/index.d.ts.map +1 -1
  27. package/lib/index.js +11 -2
  28. package/lib/index.js.map +1 -1
  29. package/lib/packageVersion.d.ts +1 -1
  30. package/lib/packageVersion.js +1 -1
  31. package/lib/packageVersion.js.map +1 -1
  32. package/lib/testObjectProvider.d.ts +4 -46
  33. package/lib/testObjectProvider.d.ts.map +1 -1
  34. package/lib/testObjectProvider.js +1 -106
  35. package/lib/testObjectProvider.js.map +1 -1
  36. package/lib/timeoutUtils.d.ts.map +1 -1
  37. package/lib/timeoutUtils.js +2 -0
  38. package/lib/timeoutUtils.js.map +1 -1
  39. package/package.json +25 -25
  40. package/src/eventAndErrorLogger.ts +164 -0
  41. package/src/index.ts +22 -8
  42. package/src/packageVersion.ts +1 -1
  43. package/src/testObjectProvider.ts +5 -155
  44. package/src/timeoutUtils.ts +8 -0
@@ -1 +1 @@
1
- {"version":3,"file":"timeoutUtils.js","sourceRoot":"","sources":["../src/timeoutUtils.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 { assert, Deferred } from \"@fluidframework/core-utils/internal\";\nimport type * as Mocha from \"mocha\";\n\nconst timeBuffer = 15; // leave 15 ms leeway for finish processing\n\n// TestTimeout class that manages tracking of test timeout. It creates a timer when timeout is in effect,\n// and provides a promise that will be rejected some time (as defined by `timeBuffer`) before the test timeout happens.\n// This will ensure that async awaits in tests do not end up timing out the tests but resolve / reject\n// before that happens.\n// Once rejected, a new TestTimeout object will be create for the timeout.\nclass TestTimeout {\n\tprivate timeout: number = 0;\n\tprivate timer: NodeJS.Timeout | undefined;\n\tprivate deferred: Deferred<void> = new Deferred<void>();\n\n\tprivate static instance: TestTimeout = new TestTimeout();\n\tpublic static updateOnYield(runnable: Mocha.Runnable): void {\n\t\tTestTimeout.instance.clearTimer();\n\t\tTestTimeout.instance.resetTimer(runnable);\n\t}\n\n\tpublic static reset(): void {\n\t\tTestTimeout.instance.clearTimer();\n\t\tTestTimeout.instance = new TestTimeout();\n\t}\n\n\tpublic static getInstance(): TestTimeout {\n\t\treturn TestTimeout.instance;\n\t}\n\n\tpublic async getPromise(): Promise<void> {\n\t\treturn this.deferred.promise;\n\t}\n\n\tpublic getTimeout(): number | undefined {\n\t\treturn this.timeout;\n\t}\n\n\tprivate constructor() {}\n\n\tprivate resetTimer(runnable: Mocha.Runnable): void {\n\t\tassert(!this.timer, \"clearTimer should have been called before reset\");\n\t\tassert(!this.deferred.isCompleted, \"can't reset a completed TestTimeout\");\n\n\t\t// Check the test timeout setting\n\t\tconst timeoutFromMochaTest = runnable.timeout();\n\t\tif (!(Number.isFinite(timeoutFromMochaTest) && timeoutFromMochaTest > 0)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// subtract a buffer\n\t\tthis.timeout = Math.max(timeoutFromMochaTest - timeBuffer, 1);\n\n\t\t// Set up timer to reject near the test timeout.\n\t\tthis.timer = setTimeout(() => {\n\t\t\tthis.deferred.reject(this);\n\t\t}, this.timeout);\n\t}\n\tprivate clearTimer(): void {\n\t\tif (this.timer) {\n\t\t\tthis.deferred = new Deferred();\n\t\t\tclearTimeout(this.timer);\n\t\t\tthis.timer = undefined;\n\t\t}\n\t}\n}\n\n// Only register if we are running with mocha-test-setup loaded (that package is what sets globalThis.getMochaModule).\nif (globalThis.getMochaModule !== undefined) {\n\t// Patch the private methods resetTimeout and clearTimeout on Mocha's runnable objects so we can do the appropriate\n\t// calls in TestTimeout above when the Mocha methods are called.\n\t// These are not part of the public API so if Mocha changes how it works, this could break.\n\t// See https://github.com/mochajs/mocha/blob/8d0ca3ed77ba8a704b2aa8b58267a084a475a51b/lib/runnable.js#L234.\n\tconst mochaModule = globalThis.getMochaModule() as typeof Mocha;\n\tconst runnablePrototype = mochaModule.Runnable.prototype;\n\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\tconst oldResetTimeoutFunc = runnablePrototype.resetTimeout;\n\tlet resetTimeoutCallDepth = 0;\n\t// Mocha invokes resetTimeout after each async yield a test performs.\n\trunnablePrototype.resetTimeout = function (this: Mocha.Runnable) {\n\t\tresetTimeoutCallDepth++;\n\t\ttry {\n\t\t\toldResetTimeoutFunc.call(this);\n\t\t} finally {\n\t\t\tresetTimeoutCallDepth--;\n\t\t}\n\t\tTestTimeout.updateOnYield(this);\n\t};\n\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\tconst oldClearTimeoutFunc = runnablePrototype.clearTimeout;\n\trunnablePrototype.clearTimeout = function (this: Mocha.Runnable) {\n\t\tif (resetTimeoutCallDepth === 0) {\n\t\t\t// Mocha's runnable invokes clearTimeout as part of its resetTimeout as well as at the end of Runnables.\n\t\t\t// We only want to fully reset the TestTimeout instance at the end of each runnable, not on JS turn boundaries.\n\t\t\tTestTimeout.reset();\n\t\t}\n\t\toldClearTimeoutFunc.call(this);\n\t};\n}\n\n/**\n * @internal\n */\nexport interface TimeoutDurationOption {\n\t/**\n\t * Timeout duration in milliseconds, if it is great than 0 and not Infinity\n\t * If it is undefined, then it will use test timeout if we are in side the test function\n\t * Otherwise, there is no timeout\n\t */\n\tdurationMs?: number;\n}\n\n/**\n * @internal\n */\nexport interface TimeoutWithError extends TimeoutDurationOption {\n\treject?: true;\n\terrorMsg?: string;\n\t// Since there are no required properties, this type explicitly\n\t// rejects `value` to avoid confusion with TimeoutWithValue.\n\tvalue?: never;\n}\n\n/**\n * @internal\n */\nexport interface TimeoutWithValue<T = void> extends TimeoutDurationOption {\n\treject: false;\n\tvalue: T;\n}\n\nexport type PromiseExecutor<T = void> = (\n\tresolve: (value: T | PromiseLike<T>) => void,\n\treject: (reason?: any) => void,\n) => void;\n\n/**\n * Wraps the given promise with another one that will complete after a specific timeout if the original promise does\n * not resolve by then.\n *\n * @remarks\n * If used inside a mocha test, it uses the test timeout by default and completes the returned promise just before\n * the test timeout hits, so that tests don't time out because of unpredictable awaits.\n * In that scenario the timeout can still be overridden via `timeoutOptions` but it's recommended to use the default value.\n *\n * @param promise - The promise to be wrapped.\n * @param timeoutOptions - Options that can be used to override the timeout and / or define the behavior when\n * the promise is not fulfilled. For example, instead of rejecting the promise, resolve with a specific value.\n * @returns A new promise that will complete when the given promise resolves or the timeout expires.\n * @internal\n */\nexport async function timeoutAwait<T = void>(\n\tpromise: PromiseLike<T>,\n\ttimeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},\n): Promise<T> {\n\treturn Promise.race([promise, timeoutPromise<T>(() => {}, timeoutOptions)]);\n}\n\n/**\n * Creates a promise from the given executor that will complete after a specific timeout.\n *\n * @remarks\n * If used inside a mocha test, it uses the test timeout by default and completes the returned promise just before\n * the test timeout hits, so that tests don't time out because of unpredictable awaits.\n * In that scenario the timeout can still be overridden via `timeoutOptions` but it's recommended to use the default value.\n *\n * @param executor - The executor for the promise.\n * @param timeoutOptions - Options that can be used to override the timeout and / or define the behavior when\n * the promise is not fulfilled. For example, instead of rejecting the promise, resolve with a specific value.\n * @returns A new promise that will complete when the given executor resolves or the timeout expires.\n * @internal\n */\nexport async function timeoutPromise<T = void>(\n\texecutor: (\n\t\tresolve: (value: T | PromiseLike<T>) => void,\n\t\treject: (reason?: any) => void,\n\t) => void,\n\ttimeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},\n): Promise<T> {\n\t// create the timeout error outside the async task, so its callstack includes\n\t// the original call site, this makes it easier to debug\n\tconst err =\n\t\ttimeoutOptions.reject === false\n\t\t\t? undefined\n\t\t\t: new Error(timeoutOptions.errorMsg ?? \"Timed out\");\n\tconst executorPromise = getTimeoutPromise(executor, timeoutOptions, err);\n\n\tconst currentTestTimeout = TestTimeout.getInstance();\n\tif (currentTestTimeout === undefined) {\n\t\treturn executorPromise;\n\t}\n\n\treturn Promise.race([executorPromise, currentTestTimeout.getPromise()]).catch((e) => {\n\t\tif (e === currentTestTimeout) {\n\t\t\tif (timeoutOptions.reject !== false) {\n\t\t\t\t// If the rejection is because of the timeout then\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tconst errorObject = err!;\n\t\t\t\terrorObject.message = `${\n\t\t\t\t\ttimeoutOptions.errorMsg ?? \"Forcing timeout before test does\"\n\t\t\t\t} (${currentTestTimeout.getTimeout()}ms)`;\n\t\t\t\tthrow errorObject;\n\t\t\t}\n\t\t\treturn timeoutOptions.value;\n\t\t}\n\t\tthrow e;\n\t}) as Promise<T>;\n}\n\n// Create a promise based on the timeout options\nasync function getTimeoutPromise<T = void>(\n\texecutor: (\n\t\tresolve: (value: T | PromiseLike<T>) => void,\n\t\treject: (reason?: any) => void,\n\t) => void,\n\ttimeoutOptions: TimeoutWithError | TimeoutWithValue<T>,\n\terr: Error | undefined,\n): Promise<T> {\n\tconst timeout = timeoutOptions.durationMs ?? 0;\n\tif (timeout <= 0 || !Number.isFinite(timeout)) {\n\t\treturn new Promise(executor);\n\t}\n\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst timeoutRejections = (): void => {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst errorObject = err!;\n\t\t\terrorObject.message = `${errorObject.message} (${timeout}ms)`;\n\t\t\treject(errorObject);\n\t\t};\n\t\tconst timer = setTimeout(\n\t\t\t() =>\n\t\t\t\ttimeoutOptions.reject === false ? resolve(timeoutOptions.value) : timeoutRejections(),\n\t\t\ttimeout,\n\t\t);\n\n\t\texecutor(\n\t\t\t(value) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(reason: Error) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(reason);\n\t\t\t},\n\t\t);\n\t});\n}\n"]}
1
+ {"version":3,"file":"timeoutUtils.js","sourceRoot":"","sources":["../src/timeoutUtils.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\n// Warning: this module has load side effect of patching Mocha's timeout handling.\n// See globalThis.getMochaModule use below.\n\nimport { assert, Deferred } from \"@fluidframework/core-utils/internal\";\n// Note that \"@types/mocha\" is only a devDependency of this package.\n// None of the mocha types are exposed through exports and thus it's reasonable\n// to leave mocha as a devDependency only. Further, the underlying code is only\n// used when @fluid-internal/mocha-test-setup is used and that package dictates\n// a mocha version.\nimport type * as Mocha from \"mocha\";\n\nconst timeBuffer = 15; // leave 15 ms leeway for finish processing\n\n// TestTimeout class that manages tracking of test timeout. It creates a timer when timeout is in effect,\n// and provides a promise that will be rejected some time (as defined by `timeBuffer`) before the test timeout happens.\n// This will ensure that async awaits in tests do not end up timing out the tests but resolve / reject\n// before that happens.\n// Once rejected, a new TestTimeout object will be create for the timeout.\nclass TestTimeout {\n\tprivate timeout: number = 0;\n\tprivate timer: NodeJS.Timeout | undefined;\n\tprivate deferred: Deferred<void> = new Deferred<void>();\n\n\tprivate static instance: TestTimeout = new TestTimeout();\n\tpublic static updateOnYield(runnable: Mocha.Runnable): void {\n\t\tTestTimeout.instance.clearTimer();\n\t\tTestTimeout.instance.resetTimer(runnable);\n\t}\n\n\tpublic static reset(): void {\n\t\tTestTimeout.instance.clearTimer();\n\t\tTestTimeout.instance = new TestTimeout();\n\t}\n\n\tpublic static getInstance(): TestTimeout {\n\t\treturn TestTimeout.instance;\n\t}\n\n\tpublic async getPromise(): Promise<void> {\n\t\treturn this.deferred.promise;\n\t}\n\n\tpublic getTimeout(): number | undefined {\n\t\treturn this.timeout;\n\t}\n\n\tprivate constructor() {}\n\n\tprivate resetTimer(runnable: Mocha.Runnable): void {\n\t\tassert(!this.timer, \"clearTimer should have been called before reset\");\n\t\tassert(!this.deferred.isCompleted, \"can't reset a completed TestTimeout\");\n\n\t\t// Check the test timeout setting\n\t\tconst timeoutFromMochaTest = runnable.timeout();\n\t\tif (!(Number.isFinite(timeoutFromMochaTest) && timeoutFromMochaTest > 0)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// subtract a buffer\n\t\tthis.timeout = Math.max(timeoutFromMochaTest - timeBuffer, 1);\n\n\t\t// Set up timer to reject near the test timeout.\n\t\tthis.timer = setTimeout(() => {\n\t\t\tthis.deferred.reject(this);\n\t\t}, this.timeout);\n\t}\n\tprivate clearTimer(): void {\n\t\tif (this.timer) {\n\t\t\tthis.deferred = new Deferred();\n\t\t\tclearTimeout(this.timer);\n\t\t\tthis.timer = undefined;\n\t\t}\n\t}\n}\n\n// Only register if we are running with mocha-test-setup loaded (that package is what sets globalThis.getMochaModule).\nif (globalThis.getMochaModule !== undefined) {\n\t// Patch the private methods resetTimeout and clearTimeout on Mocha's runnable objects so we can do the appropriate\n\t// calls in TestTimeout above when the Mocha methods are called.\n\t// These are not part of the public API so if Mocha changes how it works, this could break.\n\t// See https://github.com/mochajs/mocha/blob/8d0ca3ed77ba8a704b2aa8b58267a084a475a51b/lib/runnable.js#L234.\n\tconst mochaModule = globalThis.getMochaModule() as typeof Mocha;\n\tconst runnablePrototype = mochaModule.Runnable.prototype;\n\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\tconst oldResetTimeoutFunc = runnablePrototype.resetTimeout;\n\tlet resetTimeoutCallDepth = 0;\n\t// Mocha invokes resetTimeout after each async yield a test performs.\n\trunnablePrototype.resetTimeout = function (this: Mocha.Runnable) {\n\t\tresetTimeoutCallDepth++;\n\t\ttry {\n\t\t\toldResetTimeoutFunc.call(this);\n\t\t} finally {\n\t\t\tresetTimeoutCallDepth--;\n\t\t}\n\t\tTestTimeout.updateOnYield(this);\n\t};\n\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\tconst oldClearTimeoutFunc = runnablePrototype.clearTimeout;\n\trunnablePrototype.clearTimeout = function (this: Mocha.Runnable) {\n\t\tif (resetTimeoutCallDepth === 0) {\n\t\t\t// Mocha's runnable invokes clearTimeout as part of its resetTimeout as well as at the end of Runnables.\n\t\t\t// We only want to fully reset the TestTimeout instance at the end of each runnable, not on JS turn boundaries.\n\t\t\tTestTimeout.reset();\n\t\t}\n\t\toldClearTimeoutFunc.call(this);\n\t};\n}\n\n/**\n * @internal\n */\nexport interface TimeoutDurationOption {\n\t/**\n\t * Timeout duration in milliseconds, if it is great than 0 and not Infinity\n\t * If it is undefined, then it will use test timeout if we are in side the test function\n\t * Otherwise, there is no timeout\n\t */\n\tdurationMs?: number;\n}\n\n/**\n * @internal\n */\nexport interface TimeoutWithError extends TimeoutDurationOption {\n\treject?: true;\n\terrorMsg?: string;\n\t// Since there are no required properties, this type explicitly\n\t// rejects `value` to avoid confusion with TimeoutWithValue.\n\tvalue?: never;\n}\n\n/**\n * @internal\n */\nexport interface TimeoutWithValue<T = void> extends TimeoutDurationOption {\n\treject: false;\n\tvalue: T;\n}\n\nexport type PromiseExecutor<T = void> = (\n\tresolve: (value: T | PromiseLike<T>) => void,\n\treject: (reason?: any) => void,\n) => void;\n\n/**\n * Wraps the given promise with another one that will complete after a specific timeout if the original promise does\n * not resolve by then.\n *\n * @remarks\n * If used inside a mocha test, it uses the test timeout by default and completes the returned promise just before\n * the test timeout hits, so that tests don't time out because of unpredictable awaits.\n * In that scenario the timeout can still be overridden via `timeoutOptions` but it's recommended to use the default value.\n *\n * @param promise - The promise to be wrapped.\n * @param timeoutOptions - Options that can be used to override the timeout and / or define the behavior when\n * the promise is not fulfilled. For example, instead of rejecting the promise, resolve with a specific value.\n * @returns A new promise that will complete when the given promise resolves or the timeout expires.\n * @internal\n */\nexport async function timeoutAwait<T = void>(\n\tpromise: PromiseLike<T>,\n\ttimeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},\n): Promise<T> {\n\treturn Promise.race([promise, timeoutPromise<T>(() => {}, timeoutOptions)]);\n}\n\n/**\n * Creates a promise from the given executor that will complete after a specific timeout.\n *\n * @remarks\n * If used inside a mocha test, it uses the test timeout by default and completes the returned promise just before\n * the test timeout hits, so that tests don't time out because of unpredictable awaits.\n * In that scenario the timeout can still be overridden via `timeoutOptions` but it's recommended to use the default value.\n *\n * @param executor - The executor for the promise.\n * @param timeoutOptions - Options that can be used to override the timeout and / or define the behavior when\n * the promise is not fulfilled. For example, instead of rejecting the promise, resolve with a specific value.\n * @returns A new promise that will complete when the given executor resolves or the timeout expires.\n * @internal\n */\nexport async function timeoutPromise<T = void>(\n\texecutor: (\n\t\tresolve: (value: T | PromiseLike<T>) => void,\n\t\treject: (reason?: any) => void,\n\t) => void,\n\ttimeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},\n): Promise<T> {\n\t// create the timeout error outside the async task, so its callstack includes\n\t// the original call site, this makes it easier to debug\n\tconst err =\n\t\ttimeoutOptions.reject === false\n\t\t\t? undefined\n\t\t\t: new Error(timeoutOptions.errorMsg ?? \"Timed out\");\n\tconst executorPromise = getTimeoutPromise(executor, timeoutOptions, err);\n\n\tconst currentTestTimeout = TestTimeout.getInstance();\n\tif (currentTestTimeout === undefined) {\n\t\treturn executorPromise;\n\t}\n\n\treturn Promise.race([executorPromise, currentTestTimeout.getPromise()]).catch((e) => {\n\t\tif (e === currentTestTimeout) {\n\t\t\tif (timeoutOptions.reject !== false) {\n\t\t\t\t// If the rejection is because of the timeout then\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tconst errorObject = err!;\n\t\t\t\terrorObject.message = `${\n\t\t\t\t\ttimeoutOptions.errorMsg ?? \"Forcing timeout before test does\"\n\t\t\t\t} (${currentTestTimeout.getTimeout()}ms)`;\n\t\t\t\tthrow errorObject;\n\t\t\t}\n\t\t\treturn timeoutOptions.value;\n\t\t}\n\t\tthrow e;\n\t}) as Promise<T>;\n}\n\n// Create a promise based on the timeout options\nasync function getTimeoutPromise<T = void>(\n\texecutor: (\n\t\tresolve: (value: T | PromiseLike<T>) => void,\n\t\treject: (reason?: any) => void,\n\t) => void,\n\ttimeoutOptions: TimeoutWithError | TimeoutWithValue<T>,\n\terr: Error | undefined,\n): Promise<T> {\n\tconst timeout = timeoutOptions.durationMs ?? 0;\n\tif (timeout <= 0 || !Number.isFinite(timeout)) {\n\t\treturn new Promise(executor);\n\t}\n\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst timeoutRejections = (): void => {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst errorObject = err!;\n\t\t\terrorObject.message = `${errorObject.message} (${timeout}ms)`;\n\t\t\treject(errorObject);\n\t\t};\n\t\tconst timer = setTimeout(\n\t\t\t() =>\n\t\t\t\ttimeoutOptions.reject === false ? resolve(timeoutOptions.value) : timeoutRejections(),\n\t\t\ttimeout,\n\t\t);\n\n\t\texecutor(\n\t\t\t(value) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(reason: Error) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(reason);\n\t\t\t},\n\t\t);\n\t});\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/test-utils",
3
- "version": "2.90.0",
3
+ "version": "2.91.0",
4
4
  "description": "Utilities for Fluid tests",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -67,39 +67,38 @@
67
67
  "temp-directory": "nyc/.nyc_output"
68
68
  },
69
69
  "dependencies": {
70
- "@fluid-internal/test-driver-definitions": "~2.90.0",
71
- "@fluidframework/container-definitions": "~2.90.0",
72
- "@fluidframework/container-loader": "~2.90.0",
73
- "@fluidframework/container-runtime": "~2.90.0",
74
- "@fluidframework/container-runtime-definitions": "~2.90.0",
75
- "@fluidframework/core-interfaces": "~2.90.0",
76
- "@fluidframework/core-utils": "~2.90.0",
77
- "@fluidframework/datastore": "~2.90.0",
78
- "@fluidframework/datastore-definitions": "~2.90.0",
79
- "@fluidframework/driver-definitions": "~2.90.0",
80
- "@fluidframework/driver-utils": "~2.90.0",
81
- "@fluidframework/local-driver": "~2.90.0",
82
- "@fluidframework/map": "~2.90.0",
83
- "@fluidframework/odsp-driver": "~2.90.0",
84
- "@fluidframework/request-handler": "~2.90.0",
85
- "@fluidframework/routerlicious-driver": "~2.90.0",
86
- "@fluidframework/runtime-definitions": "~2.90.0",
87
- "@fluidframework/runtime-utils": "~2.90.0",
88
- "@fluidframework/shared-object-base": "~2.90.0",
89
- "@fluidframework/telemetry-utils": "~2.90.0",
70
+ "@fluid-internal/test-driver-definitions": "~2.91.0",
71
+ "@fluidframework/container-definitions": "~2.91.0",
72
+ "@fluidframework/container-loader": "~2.91.0",
73
+ "@fluidframework/container-runtime": "~2.91.0",
74
+ "@fluidframework/container-runtime-definitions": "~2.91.0",
75
+ "@fluidframework/core-interfaces": "~2.91.0",
76
+ "@fluidframework/core-utils": "~2.91.0",
77
+ "@fluidframework/datastore": "~2.91.0",
78
+ "@fluidframework/datastore-definitions": "~2.91.0",
79
+ "@fluidframework/driver-definitions": "~2.91.0",
80
+ "@fluidframework/driver-utils": "~2.91.0",
81
+ "@fluidframework/local-driver": "~2.91.0",
82
+ "@fluidframework/map": "~2.91.0",
83
+ "@fluidframework/odsp-driver": "~2.91.0",
84
+ "@fluidframework/request-handler": "~2.91.0",
85
+ "@fluidframework/routerlicious-driver": "~2.91.0",
86
+ "@fluidframework/runtime-definitions": "~2.91.0",
87
+ "@fluidframework/runtime-utils": "~2.91.0",
88
+ "@fluidframework/shared-object-base": "~2.91.0",
89
+ "@fluidframework/telemetry-utils": "~2.91.0",
90
90
  "best-random": "^1.0.0",
91
91
  "debug": "^4.3.4",
92
- "mocha": "^11.7.5",
93
92
  "uuid": "^11.1.0"
94
93
  },
95
94
  "devDependencies": {
96
95
  "@arethetypeswrong/cli": "^0.18.2",
97
- "@biomejs/biome": "~1.9.3",
98
- "@fluid-internal/mocha-test-setup": "~2.90.0",
96
+ "@biomejs/biome": "~2.4.5",
97
+ "@fluid-internal/mocha-test-setup": "~2.91.0",
99
98
  "@fluid-tools/build-cli": "^0.63.0",
100
99
  "@fluidframework/build-common": "^2.0.3",
101
100
  "@fluidframework/build-tools": "^0.63.0",
102
- "@fluidframework/eslint-config-fluid": "~2.90.0",
101
+ "@fluidframework/eslint-config-fluid": "~2.91.0",
103
102
  "@fluidframework/test-utils-previous": "npm:@fluidframework/test-utils@2.83.0",
104
103
  "@microsoft/api-extractor": "7.52.11",
105
104
  "@types/debug": "^4.1.5",
@@ -113,6 +112,7 @@
113
112
  "diff": "^3.5.0",
114
113
  "eslint": "~9.39.1",
115
114
  "jiti": "^2.6.1",
115
+ "mocha": "^11.7.5",
116
116
  "mocha-multi-reporters": "^1.5.1",
117
117
  "rimraf": "^6.1.3",
118
118
  "typescript": "~5.4.5"
@@ -0,0 +1,164 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type {
7
+ ITelemetryBaseEvent,
8
+ ITelemetryBaseLogger,
9
+ } from "@fluidframework/core-interfaces";
10
+ import type { ITelemetryGenericEventExt } from "@fluidframework/telemetry-utils/internal";
11
+
12
+ import { isNonEmptyArray } from "./nonEmptyArrayType.js";
13
+
14
+ /** @internal */
15
+ export interface IEventAndErrorTrackingLogger {
16
+ registerExpectedEvent: (...orderedExpectedEvents: ITelemetryGenericEventExt[]) => void;
17
+ reportAndClearTrackedEvents: () => {
18
+ expectedNotFound: { index: number; event: ITelemetryGenericEventExt }[];
19
+ unexpectedErrors: ITelemetryBaseEvent[];
20
+ };
21
+ }
22
+
23
+ /**
24
+ * This class tracks events. It allows specifying expected events, which will be looked for in order.
25
+ * It also tracks all unexpected errors.
26
+ * At any point you call reportAndClearTrackedEvents which will provide all unexpected errors, and
27
+ * any expected events that have not occurred.
28
+ * @internal
29
+ */
30
+ export class EventAndErrorTrackingLogger
31
+ implements ITelemetryBaseLogger, IEventAndErrorTrackingLogger
32
+ {
33
+ /**
34
+ * Even if these error events are logged, tests should still be allowed to pass
35
+ * Additionally, if downgrade is true, then log as generic (e.g. to avoid polluting the e2e test logs)
36
+ */
37
+ private readonly allowedErrors: { eventName: string; downgrade?: true }[] = [
38
+ // This log was removed in current version as unnecessary, but it's still present in previous versions
39
+ {
40
+ eventName: "fluid:telemetry:Container:NoRealStorageInDetachedContainer",
41
+ downgrade: true,
42
+ },
43
+ // This log's category changes depending on the op latency. test results shouldn't be affected but if we see lots we'd like an alert from the logs.
44
+ { eventName: "fluid:telemetry:OpRoundtripTime" },
45
+ ];
46
+
47
+ constructor(private readonly baseLogger?: ITelemetryBaseLogger) {}
48
+
49
+ private readonly expectedEvents: { index: number; event: ITelemetryGenericEventExt }[] = [];
50
+ private readonly unexpectedErrors: ITelemetryBaseEvent[] = [];
51
+
52
+ public registerExpectedEvent(...orderedExpectedEvents: ITelemetryGenericEventExt[]): void {
53
+ if (this.expectedEvents.length !== 0) {
54
+ // we don't have to error here. just no reason not to. given the events must be
55
+ // ordered it could be tricky to figure out problems around multiple registrations.
56
+ throw new Error(
57
+ "Expected events already registered.\n" +
58
+ "Call reportAndClearTrackedEvents to clear them before registering more",
59
+ );
60
+ }
61
+ this.expectedEvents.push(
62
+ ...orderedExpectedEvents.map((event, index) => ({ index, event })),
63
+ );
64
+ }
65
+
66
+ send(event: ITelemetryBaseEvent): void {
67
+ if (isNonEmptyArray(this.expectedEvents)) {
68
+ const ee = this.expectedEvents[0].event;
69
+ if (ee.eventName === event.eventName) {
70
+ let matches = true;
71
+ for (const key of Object.keys(ee)) {
72
+ if (ee[key] !== event[key]) {
73
+ matches = false;
74
+ break;
75
+ }
76
+ }
77
+ if (matches) {
78
+ // we found an expected event
79
+ // so remove it from the list of expected events
80
+ // and if it is an error, change it to generic
81
+ // this helps keep our telemetry clear of
82
+ // expected errors.
83
+ this.expectedEvents.shift();
84
+ if (event.category === "error") {
85
+ event.category = "generic";
86
+ }
87
+ }
88
+ }
89
+ }
90
+ if (event.category === "error") {
91
+ // Check to see if this error is allowed and if its category should be downgraded
92
+ const allowedError = this.allowedErrors.find(
93
+ ({ eventName }) => eventName === event.eventName,
94
+ );
95
+
96
+ if (allowedError === undefined) {
97
+ this.unexpectedErrors.push(event);
98
+ } else if (allowedError.downgrade) {
99
+ event.category = "generic";
100
+ }
101
+ }
102
+
103
+ this.baseLogger?.send(event);
104
+ }
105
+
106
+ public reportAndClearTrackedEvents(): {
107
+ expectedNotFound: { index: number; event: ITelemetryGenericEventExt }[];
108
+ unexpectedErrors: ITelemetryBaseEvent[];
109
+ } {
110
+ const expectedNotFound = this.expectedEvents.splice(0, this.expectedEvents.length);
111
+ const unexpectedErrors = this.unexpectedErrors.splice(0, this.unexpectedErrors.length);
112
+ return {
113
+ expectedNotFound,
114
+ unexpectedErrors,
115
+ };
116
+ }
117
+ }
118
+
119
+ /** Summarize the event with just the primary properties, for succinct output in case of test failure */
120
+ const primaryEventProps = ({
121
+ category,
122
+ eventName,
123
+ error,
124
+ errorType,
125
+ }: ITelemetryBaseEvent): Partial<ITelemetryBaseEvent> => ({
126
+ category,
127
+ eventName,
128
+ error,
129
+ errorType,
130
+ ["..."]: "*** Additional properties not shown, see full log for details ***",
131
+ });
132
+
133
+ /**
134
+ * Retrieves unexpected errors from a logger and returns them as an exception.
135
+ *
136
+ * @internal
137
+ */
138
+ export function getUnexpectedLogErrorException(
139
+ logger: IEventAndErrorTrackingLogger | undefined,
140
+ prefix?: string,
141
+ ): Error | undefined {
142
+ if (logger === undefined) {
143
+ return;
144
+ }
145
+ const results = logger.reportAndClearTrackedEvents();
146
+ if (results.unexpectedErrors.length > 0) {
147
+ return new Error(
148
+ `${prefix ?? ""}Unexpected Errors in Logs:\n${JSON.stringify(
149
+ results.unexpectedErrors.map(primaryEventProps),
150
+ undefined,
151
+ 2,
152
+ )}`,
153
+ );
154
+ }
155
+ if (results.expectedNotFound.length > 0) {
156
+ return new Error(
157
+ `${prefix ?? ""}Expected Events not found:\n${JSON.stringify(
158
+ results.expectedNotFound,
159
+ undefined,
160
+ 2,
161
+ )}`,
162
+ );
163
+ }
164
+ }
package/src/index.ts CHANGED
@@ -3,8 +3,12 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ export type { IEventAndErrorTrackingLogger } from "./eventAndErrorLogger.js";
7
+ export {
8
+ EventAndErrorTrackingLogger,
9
+ getUnexpectedLogErrorException,
10
+ } from "./eventAndErrorLogger.js";
6
11
  export { IProvideTestFluidObject, ITestFluidObject } from "./interfaces.js";
7
- export { LoaderContainerTracker } from "./loaderContainerTracker.js";
8
12
  export {
9
13
  fluidEntryPoint,
10
14
  LocalCodeLoader,
@@ -30,25 +34,33 @@ export {
30
34
  TestFluidObjectFactory,
31
35
  TestDataObjectKind,
32
36
  } from "./testFluidObject.js";
33
- export {
34
- createDocumentId,
35
- DataObjectFactoryType,
36
- EventAndErrorTrackingLogger,
37
- type IEventAndErrorTrackingLogger,
38
- getUnexpectedLogErrorException,
37
+
38
+ // #region Exports with load side-effect
39
+ // The below runtime (not "type") exports transitively or directly import
40
+ // timeoutUtils.ts, which always executes on import and may patch Mocha's timeout
41
+ // handling. That patching only takes effect when consumers use
42
+ // @fluid-internal/mocha-test-setup that sets globalThis.getMochaModule.
43
+ // @fluid-internal/mocha-test-setup is pervasive in our tests and thus patch
44
+ // is usually in effect (when this package is used).
45
+ export { LoaderContainerTracker } from "./loaderContainerTracker.js";
46
+ export type {
39
47
  IDocumentIdStrategy,
40
48
  IOpProcessingController,
41
49
  ITestContainerConfig,
42
50
  ITestObjectProvider,
51
+ } from "./testObjectProvider.js";
52
+ export {
53
+ createDocumentId,
54
+ DataObjectFactoryType,
43
55
  TestObjectProvider,
44
56
  TestObjectProviderWithVersionedLoad,
45
57
  } from "./testObjectProvider.js";
58
+ export type { SummaryInfo } from "./TestSummaryUtils.js";
46
59
  export {
47
60
  createSummarizer,
48
61
  createSummarizerCore,
49
62
  createSummarizerFromFactory,
50
63
  summarizeNow,
51
- SummaryInfo,
52
64
  } from "./TestSummaryUtils.js";
53
65
  export {
54
66
  timeoutAwait,
@@ -63,6 +75,8 @@ export {
63
75
  getContainerEntryPointBackCompat,
64
76
  getDataStoreEntryPointBackCompat,
65
77
  } from "./containerUtils.js";
78
+ // #endregion
79
+
66
80
  export {
67
81
  type ContainerRuntimeFactoryWithDefaultDataStoreConstructor,
68
82
  type ContainerRuntimeFactoryWithDefaultDataStoreProps,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/test-utils";
9
- export const pkgVersion = "2.90.0";
9
+ export const pkgVersion = "2.91.0";
@@ -18,7 +18,6 @@ import {
18
18
  import type { IContainerRuntimeOptionsInternal } from "@fluidframework/container-runtime/internal";
19
19
  import {
20
20
  IRequestHeader,
21
- ITelemetryBaseEvent,
22
21
  ITelemetryBaseLogger,
23
22
  ITelemetryBaseProperties,
24
23
  TelemetryBaseEventPropertyType,
@@ -32,7 +31,6 @@ import {
32
31
  import { isOdspResolvedUrl } from "@fluidframework/odsp-driver/internal";
33
32
  import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions/internal";
34
33
  import {
35
- type ITelemetryGenericEventExt,
36
34
  createChildLogger,
37
35
  createMultiSinkLogger,
38
36
  type ITelemetryLoggerPropertyBags,
@@ -41,10 +39,14 @@ import {
41
39
  } from "@fluidframework/telemetry-utils/internal";
42
40
  import { v4 as uuid } from "uuid";
43
41
 
42
+ import type { IEventAndErrorTrackingLogger } from "./eventAndErrorLogger.js";
43
+ import {
44
+ EventAndErrorTrackingLogger,
45
+ getUnexpectedLogErrorException,
46
+ } from "./eventAndErrorLogger.js";
44
47
  import { LoaderContainerTracker } from "./loaderContainerTracker.js";
45
48
  import { LocalCodeLoader, fluidEntryPoint } from "./localCodeLoader.js";
46
49
  import { createAndAttachContainer } from "./localLoader.js";
47
- import { isNonEmptyArray } from "./nonEmptyArrayType.js";
48
50
  import { ChannelFactoryRegistry } from "./testFluidObject.js";
49
51
 
50
52
  const defaultCodeDetails: IFluidCodeDetails = {
@@ -329,111 +331,6 @@ function getDocumentIdStrategy(type?: TestDriverTypes): IDocumentIdStrategy {
329
331
  }
330
332
  }
331
333
 
332
- /** @internal */
333
- export interface IEventAndErrorTrackingLogger {
334
- registerExpectedEvent: (...orderedExpectedEvents: ITelemetryGenericEventExt[]) => void;
335
- reportAndClearTrackedEvents: () => {
336
- expectedNotFound: { index: number; event: ITelemetryGenericEventExt }[];
337
- unexpectedErrors: ITelemetryBaseEvent[];
338
- };
339
- }
340
-
341
- /**
342
- * This class tracks events. It allows specifying expected events, which will be looked for in order.
343
- * It also tracks all unexpected errors.
344
- * At any point you call reportAndClearTrackedEvents which will provide all unexpected errors, and
345
- * any expected events that have not occurred.
346
- * @internal
347
- */
348
- export class EventAndErrorTrackingLogger
349
- implements ITelemetryBaseLogger, IEventAndErrorTrackingLogger
350
- {
351
- /**
352
- * Even if these error events are logged, tests should still be allowed to pass
353
- * Additionally, if downgrade is true, then log as generic (e.g. to avoid polluting the e2e test logs)
354
- */
355
- private readonly allowedErrors: { eventName: string; downgrade?: true }[] = [
356
- // This log was removed in current version as unnecessary, but it's still present in previous versions
357
- {
358
- eventName: "fluid:telemetry:Container:NoRealStorageInDetachedContainer",
359
- downgrade: true,
360
- },
361
- // This log's category changes depending on the op latency. test results shouldn't be affected but if we see lots we'd like an alert from the logs.
362
- { eventName: "fluid:telemetry:OpRoundtripTime" },
363
- ];
364
-
365
- constructor(private readonly baseLogger?: ITelemetryBaseLogger) {}
366
-
367
- private readonly expectedEvents: { index: number; event: ITelemetryGenericEventExt }[] = [];
368
- private readonly unexpectedErrors: ITelemetryBaseEvent[] = [];
369
-
370
- public registerExpectedEvent(...orderedExpectedEvents: ITelemetryGenericEventExt[]): void {
371
- if (this.expectedEvents.length !== 0) {
372
- // we don't have to error here. just no reason not to. given the events must be
373
- // ordered it could be tricky to figure out problems around multiple registrations.
374
- throw new Error(
375
- "Expected events already registered.\n" +
376
- "Call reportAndClearTrackedEvents to clear them before registering more",
377
- );
378
- }
379
- this.expectedEvents.push(
380
- ...orderedExpectedEvents.map((event, index) => ({ index, event })),
381
- );
382
- }
383
-
384
- send(event: ITelemetryBaseEvent): void {
385
- if (isNonEmptyArray(this.expectedEvents)) {
386
- const ee = this.expectedEvents[0].event;
387
- if (ee.eventName === event.eventName) {
388
- let matches = true;
389
- for (const key of Object.keys(ee)) {
390
- if (ee[key] !== event[key]) {
391
- matches = false;
392
- break;
393
- }
394
- }
395
- if (matches) {
396
- // we found an expected event
397
- // so remove it from the list of expected events
398
- // and if it is an error, change it to generic
399
- // this helps keep our telemetry clear of
400
- // expected errors.
401
- this.expectedEvents.shift();
402
- if (event.category === "error") {
403
- event.category = "generic";
404
- }
405
- }
406
- }
407
- }
408
- if (event.category === "error") {
409
- // Check to see if this error is allowed and if its category should be downgraded
410
- const allowedError = this.allowedErrors.find(
411
- ({ eventName }) => eventName === event.eventName,
412
- );
413
-
414
- if (allowedError === undefined) {
415
- this.unexpectedErrors.push(event);
416
- } else if (allowedError.downgrade) {
417
- event.category = "generic";
418
- }
419
- }
420
-
421
- this.baseLogger?.send(event);
422
- }
423
-
424
- public reportAndClearTrackedEvents(): {
425
- expectedNotFound: { index: number; event: ITelemetryGenericEventExt }[];
426
- unexpectedErrors: ITelemetryBaseEvent[];
427
- } {
428
- const expectedNotFound = this.expectedEvents.splice(0, this.expectedEvents.length);
429
- const unexpectedErrors = this.unexpectedErrors.splice(0, this.unexpectedErrors.length);
430
- return {
431
- expectedNotFound,
432
- unexpectedErrors,
433
- };
434
- }
435
- }
436
-
437
334
  /**
438
335
  * Shared base class for test object provider. Contain code for loader and container creation and loading
439
336
  * @internal
@@ -1171,50 +1068,3 @@ function getUrlTelemetryProps(
1171
1068
 
1172
1069
  return tagData(TelemetryDataTag.UserData, props);
1173
1070
  }
1174
-
1175
- /** Summarize the event with just the primary properties, for succinct output in case of test failure */
1176
- const primaryEventProps = ({
1177
- category,
1178
- eventName,
1179
- error,
1180
- errorType,
1181
- }: ITelemetryBaseEvent): Partial<ITelemetryBaseEvent> => ({
1182
- category,
1183
- eventName,
1184
- error,
1185
- errorType,
1186
- ["..."]: "*** Additional properties not shown, see full log for details ***",
1187
- });
1188
-
1189
- /**
1190
- * Retrieves unexpected errors from a logger and returns them as an exception.
1191
- *
1192
- * @internal
1193
- */
1194
- export function getUnexpectedLogErrorException(
1195
- logger: IEventAndErrorTrackingLogger | undefined,
1196
- prefix?: string,
1197
- ): Error | undefined {
1198
- if (logger === undefined) {
1199
- return;
1200
- }
1201
- const results = logger.reportAndClearTrackedEvents();
1202
- if (results.unexpectedErrors.length > 0) {
1203
- return new Error(
1204
- `${prefix ?? ""}Unexpected Errors in Logs:\n${JSON.stringify(
1205
- results.unexpectedErrors.map(primaryEventProps),
1206
- undefined,
1207
- 2,
1208
- )}`,
1209
- );
1210
- }
1211
- if (results.expectedNotFound.length > 0) {
1212
- return new Error(
1213
- `${prefix ?? ""}Expected Events not found:\n${JSON.stringify(
1214
- results.expectedNotFound,
1215
- undefined,
1216
- 2,
1217
- )}`,
1218
- );
1219
- }
1220
- }
@@ -3,7 +3,15 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ // Warning: this module has load side effect of patching Mocha's timeout handling.
7
+ // See globalThis.getMochaModule use below.
8
+
6
9
  import { assert, Deferred } from "@fluidframework/core-utils/internal";
10
+ // Note that "@types/mocha" is only a devDependency of this package.
11
+ // None of the mocha types are exposed through exports and thus it's reasonable
12
+ // to leave mocha as a devDependency only. Further, the underlying code is only
13
+ // used when @fluid-internal/mocha-test-setup is used and that package dictates
14
+ // a mocha version.
7
15
  import type * as Mocha from "mocha";
8
16
 
9
17
  const timeBuffer = 15; // leave 15 ms leeway for finish processing