@fluidframework/test-utils 2.0.0-dev.2.3.0.115467 → 2.0.0-dev.3.1.0.125672

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 (68) hide show
  1. package/.eslintrc.js +8 -10
  2. package/README.md +41 -11
  3. package/api-extractor.json +2 -2
  4. package/dist/DriverWrappers.d.ts.map +1 -1
  5. package/dist/DriverWrappers.js.map +1 -1
  6. package/dist/TestConfigs.d.ts.map +1 -1
  7. package/dist/TestConfigs.js +3 -2
  8. package/dist/TestConfigs.js.map +1 -1
  9. package/dist/TestSummaryUtils.d.ts +8 -3
  10. package/dist/TestSummaryUtils.d.ts.map +1 -1
  11. package/dist/TestSummaryUtils.js +23 -23
  12. package/dist/TestSummaryUtils.js.map +1 -1
  13. package/dist/containerUtils.d.ts +38 -0
  14. package/dist/containerUtils.d.ts.map +1 -0
  15. package/dist/containerUtils.js +58 -0
  16. package/dist/containerUtils.js.map +1 -0
  17. package/dist/index.d.ts +5 -4
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +4 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/interfaces.d.ts.map +1 -1
  22. package/dist/interfaces.js.map +1 -1
  23. package/dist/loaderContainerTracker.d.ts +0 -4
  24. package/dist/loaderContainerTracker.d.ts.map +1 -1
  25. package/dist/loaderContainerTracker.js +39 -38
  26. package/dist/loaderContainerTracker.js.map +1 -1
  27. package/dist/localCodeLoader.d.ts.map +1 -1
  28. package/dist/localCodeLoader.js.map +1 -1
  29. package/dist/localLoader.d.ts.map +1 -1
  30. package/dist/localLoader.js.map +1 -1
  31. package/dist/packageVersion.d.ts +1 -1
  32. package/dist/packageVersion.js +1 -1
  33. package/dist/packageVersion.js.map +1 -1
  34. package/dist/retry.d.ts.map +1 -1
  35. package/dist/retry.js.map +1 -1
  36. package/dist/testContainerRuntimeFactory.d.ts +2 -2
  37. package/dist/testContainerRuntimeFactory.d.ts.map +1 -1
  38. package/dist/testContainerRuntimeFactory.js +3 -3
  39. package/dist/testContainerRuntimeFactory.js.map +1 -1
  40. package/dist/testFluidObject.d.ts.map +1 -1
  41. package/dist/testFluidObject.js +7 -3
  42. package/dist/testFluidObject.js.map +1 -1
  43. package/dist/testObjectProvider.d.ts +2 -0
  44. package/dist/testObjectProvider.d.ts.map +1 -1
  45. package/dist/testObjectProvider.js +15 -10
  46. package/dist/testObjectProvider.js.map +1 -1
  47. package/dist/timeoutUtils.d.ts +11 -0
  48. package/dist/timeoutUtils.d.ts.map +1 -1
  49. package/dist/timeoutUtils.js +121 -11
  50. package/dist/timeoutUtils.js.map +1 -1
  51. package/package.json +38 -30
  52. package/prettier.config.cjs +1 -1
  53. package/src/DriverWrappers.ts +40 -37
  54. package/src/TestConfigs.ts +9 -7
  55. package/src/TestSummaryUtils.ts +126 -120
  56. package/src/containerUtils.ts +61 -0
  57. package/src/index.ts +27 -21
  58. package/src/interfaces.ts +10 -7
  59. package/src/loaderContainerTracker.ts +627 -576
  60. package/src/localCodeLoader.ts +85 -77
  61. package/src/localLoader.ts +24 -24
  62. package/src/packageVersion.ts +1 -1
  63. package/src/retry.ts +31 -25
  64. package/src/testContainerRuntimeFactory.ts +59 -56
  65. package/src/testFluidObject.ts +168 -152
  66. package/src/testObjectProvider.ts +433 -383
  67. package/src/timeoutUtils.ts +196 -39
  68. package/tsconfig.json +9 -12
@@ -1 +1 @@
1
- {"version":3,"file":"timeoutUtils.js","sourceRoot":"","sources":["../src/timeoutUtils.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAIU,QAAA,wBAAwB,GAAG,GAAG,CAAC;AAarC,KAAK,UAAU,YAAY,CAC9B,OAAuB,EACvB,iBAAyD,EAAE;IAE3D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,cAAc,CAAI,GAAG,EAAE,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;AACjF,CAAC;AALD,oCAKC;AAEM,KAAK,UAAU,wBAAwB,CAAC,SAAoB;IAC/D,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;QACtB,OAAO,cAAc,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;KACpF;AACL,CAAC;AAJD,4DAIC;AAEM,KAAK,UAAU,cAAc,CAChC,QAAgG,EAChG,iBAAyD,EAAE;;IAE3D,MAAM,OAAO,GACT,cAAc,CAAC,UAAU,KAAK,SAAS;WACpC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC;WAC1C,cAAc,CAAC,UAAU,GAAG,CAAC;QAC5B,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,gCAAwB,CAAC;IAC/D,6EAA6E;IAC7E,wDAAwD;IACxD,MAAM,GAAG,GAAG,cAAc,CAAC,MAAM,KAAK,KAAK;QACvC,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,MAAA,cAAc,CAAC,QAAQ,mCAAI,WAAW,IAAI,OAAO,KAAK,CAAC,CAAC;IAC3E,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,MAAM,KAAK,GAAG,UAAU,CACpB,GAAG,EAAE,CAAC,cAAc,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EACnF,OAAO,CAAC,CAAC;QAEb,QAAQ,CACJ,CAAC,KAAK,EAAE,EAAE;YACN,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC,EACD,CAAC,MAAM,EAAE,EAAE;YACP,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;AACP,CAAC;AA7BD,wCA6BC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { Container } from \"@fluidframework/container-loader\";\n\nexport const defaultTimeoutDurationMs = 250;\n\nexport interface TimeoutWithError {\n durationMs?: number;\n reject?: true;\n errorMsg?: string;\n}\nexport interface TimeoutWithValue<T = void> {\n durationMs?: number;\n reject: false;\n value: T;\n}\n\nexport async function timeoutAwait<T = void>(\n promise: PromiseLike<T>,\n timeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},\n) {\n return Promise.race([promise, timeoutPromise<T>(() => { }, timeoutOptions)]);\n}\n\nexport async function ensureContainerConnected(container: Container): Promise<void> {\n if (!container.connected) {\n return timeoutPromise((resolve) => container.once(\"connected\", () => resolve()));\n }\n}\n\nexport async function timeoutPromise<T = void>(\n executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void,\n timeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},\n): Promise<T> {\n const timeout =\n timeoutOptions.durationMs !== undefined\n && Number.isFinite(timeoutOptions.durationMs)\n && timeoutOptions.durationMs > 0\n ? timeoutOptions.durationMs : defaultTimeoutDurationMs;\n // create the timeout error outside the async task, so its callstack includes\n // the original call site, this makes it easier to debug\n const err = timeoutOptions.reject === false\n ? undefined\n : new Error(`${timeoutOptions.errorMsg ?? \"Timed out\"}(${timeout}ms)`);\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(\n () => timeoutOptions.reject === false ? resolve(timeoutOptions.value) : reject(err),\n timeout);\n\n executor(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (reason) => {\n clearTimeout(timer);\n reject(reason);\n });\n });\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\nimport { Container } from \"@fluidframework/container-loader\";\nimport { assert, Deferred } from \"@fluidframework/common-utils\";\n\n// @deprecated this value is no longer used\nexport const defaultTimeoutDurationMs = 250;\n\n// TestTimeout class manage tracking of test timeout. It create a timer when timeout is in effect,\n// and provide a promise that will be reject before the test timeout happen with a `timeBuffer` of 15 ms.\n// Once rejected, a new TestTimeout object will be create for the timeout.\n\nconst timeBuffer = 15; // leave 15 ms leeway for finish processing\n\nclass TestTimeout {\n\tprivate timeout: number = 0;\n\tprivate timer: NodeJS.Timeout | undefined;\n\tprivate readonly deferred: Deferred<void>;\n\tprivate rejected = false;\n\n\tprivate static instance: TestTimeout = new TestTimeout();\n\tpublic static reset(runnable: Mocha.Runnable) {\n\t\tTestTimeout.clear();\n\t\tTestTimeout.instance.resetTimer(runnable);\n\t}\n\n\tpublic static clear() {\n\t\tif (TestTimeout.instance.rejected) {\n\t\t\tTestTimeout.instance = new TestTimeout();\n\t\t} else {\n\t\t\tTestTimeout.instance.clearTimer();\n\t\t}\n\t}\n\n\tpublic static getInstance() {\n\t\treturn TestTimeout.instance;\n\t}\n\n\tpublic async getPromise() {\n\t\treturn this.deferred.promise;\n\t}\n\n\tpublic getTimeout() {\n\t\treturn this.timeout;\n\t}\n\n\tprivate constructor() {\n\t\tthis.deferred = new Deferred();\n\t\t// Ignore rejection for timeout promise if no one is waiting for it.\n\t\tthis.deferred.promise.catch(() => {});\n\t}\n\n\tprivate resetTimer(runnable: Mocha.Runnable) {\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 timeout = runnable.timeout();\n\t\tif (!(Number.isFinite(timeout) && timeout > 0)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// subtract a buffer\n\t\tthis.timeout = Math.max(timeout - 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\tthis.rejected = true;\n\t\t}, this.timeout);\n\t}\n\tprivate clearTimer() {\n\t\tif (this.timer) {\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\nif (globalThis.getMochaModule !== undefined) {\n\t// patching resetTimeout and clearTimeout on the runnable object\n\t// so we can track when test timeout are enforced\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\trunnablePrototype.resetTimeout = function (this: Mocha.Runnable) {\n\t\toldResetTimeoutFunc.call(this);\n\t\tTestTimeout.reset(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\tTestTimeout.clear();\n\t\toldClearTimeoutFunc.call(this);\n\t};\n}\n\nexport interface TimeoutWithError {\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\treject?: true;\n\terrorMsg?: string;\n}\nexport interface TimeoutWithValue<T = void> {\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\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\nexport async function timeoutAwait<T = void>(\n\tpromise: PromiseLike<T>,\n\ttimeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},\n) {\n\treturn Promise.race([promise, timeoutPromise<T>(() => {}, timeoutOptions)]);\n}\n\nexport async function ensureContainerConnected(container: Container): Promise<void> {\n\tif (!container.connected) {\n\t\treturn timeoutPromise((resolve) => container.once(\"connected\", () => resolve()));\n\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) {\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 = () => {\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(err);\n\t\t};\n\t\tconst timer = setTimeout(\n\t\t\t() =>\n\t\t\t\ttimeoutOptions.reject === false\n\t\t\t\t\t? resolve(timeoutOptions.value)\n\t\t\t\t\t: 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) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(reason);\n\t\t\t},\n\t\t);\n\t});\n}\n\n// Create a promise based on test timeout and the timeout options\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 ?? \"Test timed out\"\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/test-utils",
3
- "version": "2.0.0-dev.2.3.0.115467",
3
+ "version": "2.0.0-dev.3.1.0.125672",
4
4
  "description": "Utilities for Fluid tests",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -28,10 +28,13 @@
28
28
  "eslint": "eslint --format stylish src",
29
29
  "eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
30
30
  "format": "npm run prettier:fix",
31
- "lint": "npm run eslint",
32
- "lint:fix": "npm run eslint:fix",
31
+ "lint": "npm run prettier && npm run eslint",
32
+ "lint:fix": "npm run prettier:fix && npm run eslint:fix",
33
33
  "prettier": "prettier --check . --ignore-path ../../../.prettierignore",
34
34
  "prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
35
+ "test": "npm run test:mocha",
36
+ "test:mocha": "mocha --unhandled-rejections=strict --recursive dist/test/*.spec.js --exit --project src/test/tsconfig.json -r node_modules/@fluidframework/mocha-test-setup",
37
+ "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
35
38
  "tsc": "tsc",
36
39
  "typetests:gen": "flub generate typetests --generate --dir .",
37
40
  "typetests:prepare": "flub generate typetests --prepare --dir . --pin"
@@ -57,46 +60,50 @@
57
60
  "temp-directory": "nyc/.nyc_output"
58
61
  },
59
62
  "dependencies": {
60
- "@fluidframework/aqueduct": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
63
+ "@fluidframework/aqueduct": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
61
64
  "@fluidframework/common-definitions": "^0.20.1",
62
65
  "@fluidframework/common-utils": "^1.0.0",
63
- "@fluidframework/container-definitions": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
64
- "@fluidframework/container-loader": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
65
- "@fluidframework/container-runtime": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
66
- "@fluidframework/container-runtime-definitions": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
67
- "@fluidframework/core-interfaces": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
68
- "@fluidframework/datastore": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
69
- "@fluidframework/datastore-definitions": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
70
- "@fluidframework/driver-definitions": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
71
- "@fluidframework/driver-utils": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
72
- "@fluidframework/local-driver": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
73
- "@fluidframework/map": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
66
+ "@fluidframework/container-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
67
+ "@fluidframework/container-loader": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
68
+ "@fluidframework/container-runtime": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
69
+ "@fluidframework/container-runtime-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
70
+ "@fluidframework/core-interfaces": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
71
+ "@fluidframework/datastore": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
72
+ "@fluidframework/datastore-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
73
+ "@fluidframework/driver-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
74
+ "@fluidframework/driver-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
75
+ "@fluidframework/local-driver": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
76
+ "@fluidframework/map": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
77
+ "@fluidframework/mocha-test-setup": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
74
78
  "@fluidframework/protocol-definitions": "^1.1.0",
75
- "@fluidframework/request-handler": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
76
- "@fluidframework/routerlicious-driver": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
77
- "@fluidframework/runtime-definitions": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
78
- "@fluidframework/runtime-utils": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
79
- "@fluidframework/telemetry-utils": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
80
- "@fluidframework/test-driver-definitions": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
81
- "@fluidframework/test-runtime-utils": ">=2.0.0-dev.2.3.0.115467 <2.0.0-dev.3.0.0",
79
+ "@fluidframework/request-handler": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
80
+ "@fluidframework/routerlicious-driver": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
81
+ "@fluidframework/runtime-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
82
+ "@fluidframework/runtime-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
83
+ "@fluidframework/telemetry-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
84
+ "@fluidframework/test-driver-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
85
+ "@fluidframework/test-runtime-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
82
86
  "best-random": "^1.0.0",
83
87
  "debug": "^4.1.1",
84
88
  "uuid": "^8.3.1"
85
89
  },
86
90
  "devDependencies": {
87
- "@fluid-tools/build-cli": "^0.7.0",
91
+ "@fluid-tools/build-cli": "^0.8.0",
88
92
  "@fluidframework/build-common": "^1.1.0",
89
- "@fluidframework/build-tools": "^0.7.0",
90
- "@fluidframework/eslint-config-fluid": "^1.2.0",
91
- "@fluidframework/test-utils-previous": "npm:@fluidframework/test-utils@2.0.0-internal.2.2.0",
93
+ "@fluidframework/build-tools": "^0.8.0",
94
+ "@fluidframework/eslint-config-fluid": "^2.0.0",
95
+ "@fluidframework/test-utils-previous": "npm:@fluidframework/test-utils@2.0.0-internal.3.0.0",
92
96
  "@microsoft/api-extractor": "^7.22.2",
93
97
  "@rushstack/eslint-config": "^2.5.1",
98
+ "@types/debug": "^4.1.5",
94
99
  "@types/diff": "^3.5.1",
95
100
  "@types/mocha": "^9.1.1",
96
- "@types/node": "^14.18.0",
101
+ "@types/node": "^14.18.36",
97
102
  "@types/random-js": "^1.0.31",
103
+ "@types/uuid": "^8.3.0",
98
104
  "concurrently": "^6.2.0",
99
105
  "copyfiles": "^2.4.1",
106
+ "cross-env": "^7.0.2",
100
107
  "diff": "^3.5.0",
101
108
  "eslint": "~8.6.0",
102
109
  "mocha": "^10.0.0",
@@ -107,9 +114,10 @@
107
114
  "typescript": "~4.5.5"
108
115
  },
109
116
  "typeValidation": {
110
- "version": "2.0.0-internal.2.3.0",
111
- "baselineRange": ">=2.0.0-internal.2.2.0 <2.0.0-internal.2.3.0",
112
- "baselineVersion": "2.0.0-internal.2.2.0",
117
+ "version": "2.0.0-internal.3.1.0",
118
+ "previousVersionStyle": "~previousMinor",
119
+ "baselineRange": ">=2.0.0-internal.3.0.0 <2.0.0-internal.3.1.0",
120
+ "baselineVersion": "2.0.0-internal.3.0.0",
113
121
  "broken": {}
114
122
  }
115
123
  }
@@ -4,5 +4,5 @@
4
4
  */
5
5
 
6
6
  module.exports = {
7
- ...require("@fluidframework/build-common/prettier.config.cjs"),
7
+ ...require("@fluidframework/build-common/prettier.config.cjs"),
8
8
  };
@@ -5,11 +5,11 @@
5
5
 
6
6
  import { ITelemetryBaseLogger } from "@fluidframework/common-definitions";
7
7
  import {
8
- IDocumentService,
9
- IDocumentServiceFactory,
10
- IDocumentStorageService,
11
- IResolvedUrl,
12
- ISummaryContext,
8
+ IDocumentService,
9
+ IDocumentServiceFactory,
10
+ IDocumentStorageService,
11
+ IResolvedUrl,
12
+ ISummaryContext,
13
13
  } from "@fluidframework/driver-definitions";
14
14
  import { ISummaryTree } from "@fluidframework/protocol-definitions";
15
15
 
@@ -19,18 +19,18 @@ import { ISummaryTree } from "@fluidframework/protocol-definitions";
19
19
  * callback before it is uploaded to the server.
20
20
  */
21
21
  export function wrapDocumentStorageService(
22
- innerDocStorageService: IDocumentStorageService,
23
- uploadSummaryCb: (summaryTree: ISummaryTree, context: ISummaryContext) => ISummaryContext,
22
+ innerDocStorageService: IDocumentStorageService,
23
+ uploadSummaryCb: (summaryTree: ISummaryTree, context: ISummaryContext) => ISummaryContext,
24
24
  ) {
25
- const outerDocStorageService = Object.create(innerDocStorageService) as IDocumentStorageService;
26
- outerDocStorageService.uploadSummaryWithContext = async (
27
- summary: ISummaryTree,
28
- context: ISummaryContext,
29
- ): Promise<string> => {
30
- const newContext = uploadSummaryCb(summary, context);
31
- return innerDocStorageService.uploadSummaryWithContext(summary, newContext);
32
- };
33
- return outerDocStorageService;
25
+ const outerDocStorageService = Object.create(innerDocStorageService) as IDocumentStorageService;
26
+ outerDocStorageService.uploadSummaryWithContext = async (
27
+ summary: ISummaryTree,
28
+ context: ISummaryContext,
29
+ ): Promise<string> => {
30
+ const newContext = uploadSummaryCb(summary, context);
31
+ return innerDocStorageService.uploadSummaryWithContext(summary, newContext);
32
+ };
33
+ return outerDocStorageService;
34
34
  }
35
35
 
36
36
  /**
@@ -41,15 +41,15 @@ export function wrapDocumentStorageService(
41
41
  * to pass in the `uploadSummaryCb`.
42
42
  */
43
43
  export function wrapDocumentService(
44
- innerDocService: IDocumentService,
45
- uploadSummaryCb: (summaryTree: ISummaryTree, context: ISummaryContext) => ISummaryContext,
44
+ innerDocService: IDocumentService,
45
+ uploadSummaryCb: (summaryTree: ISummaryTree, context: ISummaryContext) => ISummaryContext,
46
46
  ) {
47
- const outerDocService = Object.create(innerDocService) as IDocumentService;
48
- outerDocService.connectToStorage = async (): Promise<IDocumentStorageService> => {
49
- const storageService = await innerDocService.connectToStorage();
50
- return wrapDocumentStorageService(storageService, uploadSummaryCb);
51
- };
52
- return outerDocService;
47
+ const outerDocService = Object.create(innerDocService) as IDocumentService;
48
+ outerDocService.connectToStorage = async (): Promise<IDocumentStorageService> => {
49
+ const storageService = await innerDocService.connectToStorage();
50
+ return wrapDocumentStorageService(storageService, uploadSummaryCb);
51
+ };
52
+ return outerDocService;
53
53
  }
54
54
 
55
55
  /**
@@ -60,18 +60,21 @@ export function wrapDocumentService(
60
60
  * pass in the `uploadSummaryCb`.
61
61
  */
62
62
  export function wrapDocumentServiceFactory(
63
- innerDocServiceFactory: IDocumentServiceFactory,
64
- uploadSummaryCb: (summaryTree: ISummaryTree, context: ISummaryContext) => ISummaryContext,
63
+ innerDocServiceFactory: IDocumentServiceFactory,
64
+ uploadSummaryCb: (summaryTree: ISummaryTree, context: ISummaryContext) => ISummaryContext,
65
65
  ) {
66
- const outerDocServiceFactory = Object.create(innerDocServiceFactory) as IDocumentServiceFactory;
67
- outerDocServiceFactory.createDocumentService = async (
68
- resolvedUrl: IResolvedUrl,
69
- logger?: ITelemetryBaseLogger,
70
- clientIsSummarizer?: boolean,
71
- ): Promise<IDocumentService> => {
72
- const documentService = await innerDocServiceFactory.createDocumentService(
73
- resolvedUrl, logger, clientIsSummarizer);
74
- return wrapDocumentService(documentService, uploadSummaryCb);
75
- };
76
- return outerDocServiceFactory;
66
+ const outerDocServiceFactory = Object.create(innerDocServiceFactory) as IDocumentServiceFactory;
67
+ outerDocServiceFactory.createDocumentService = async (
68
+ resolvedUrl: IResolvedUrl,
69
+ logger?: ITelemetryBaseLogger,
70
+ clientIsSummarizer?: boolean,
71
+ ): Promise<IDocumentService> => {
72
+ const documentService = await innerDocServiceFactory.createDocumentService(
73
+ resolvedUrl,
74
+ logger,
75
+ clientIsSummarizer,
76
+ );
77
+ return wrapDocumentService(documentService, uploadSummaryCb);
78
+ };
79
+ return outerDocServiceFactory;
77
80
  }
@@ -5,10 +5,12 @@
5
5
 
6
6
  import { ConfigTypes, IConfigProviderBase } from "@fluidframework/telemetry-utils";
7
7
 
8
- export const mockConfigProvider = ((settings: Record<string, ConfigTypes> = {}): IConfigProviderBase => {
9
- settings["Fluid.GarbageCollection.TrackGCState"] = "true";
10
- settings["Fluid.GarbageCollection.WriteDataAtRoot"] = "true";
11
- return {
12
- getRawConfig: (name: string): ConfigTypes => settings[name],
13
- };
14
- });
8
+ export const mockConfigProvider = (
9
+ settings: Record<string, ConfigTypes> = {},
10
+ ): IConfigProviderBase => {
11
+ settings["Fluid.GarbageCollection.TrackGCState"] = "true";
12
+ settings["Fluid.GarbageCollection.WriteDataAtRoot"] = "true";
13
+ return {
14
+ getRawConfig: (name: string): ConfigTypes => settings[name],
15
+ };
16
+ };
@@ -6,158 +6,164 @@
6
6
  import { ContainerRuntimeFactoryWithDefaultDataStore } from "@fluidframework/aqueduct";
7
7
  import { assert } from "@fluidframework/common-utils";
8
8
  import { IContainer, IHostLoader, LoaderHeader } from "@fluidframework/container-definitions";
9
- import { ConnectionState } from "@fluidframework/container-loader";
10
9
  import {
11
- IGCRuntimeOptions,
12
- ISummarizer,
13
- ISummaryRuntimeOptions,
10
+ IGCRuntimeOptions,
11
+ ISummarizer,
12
+ ISummaryRuntimeOptions,
14
13
  } from "@fluidframework/container-runtime";
15
14
  import { FluidObject, IRequest } from "@fluidframework/core-interfaces";
16
15
  import { DriverHeader } from "@fluidframework/driver-definitions";
17
16
  import {
18
- IContainerRuntimeBase,
19
- IFluidDataStoreFactory,
20
- NamedFluidDataStoreRegistryEntries,
17
+ IContainerRuntimeBase,
18
+ IFluidDataStoreFactory,
19
+ NamedFluidDataStoreRegistryEntries,
21
20
  } from "@fluidframework/runtime-definitions";
22
21
  import { requestFluidObject } from "@fluidframework/runtime-utils";
23
22
  import { IConfigProviderBase } from "@fluidframework/telemetry-utils";
23
+ import { ITelemetryBaseLogger } from "@fluidframework/common-definitions";
24
24
  import { ITestContainerConfig, ITestObjectProvider } from "./testObjectProvider";
25
25
  import { mockConfigProvider } from "./TestConfigs";
26
+ import { waitForContainerConnection } from "./containerUtils";
27
+ import { timeoutAwait } from "./timeoutUtils";
26
28
 
27
29
  const summarizerClientType = "summarizer";
28
30
 
29
- async function createSummarizerCore(absoluteUrl: string | undefined, loader: IHostLoader, summaryVersion?: string) {
30
- if (absoluteUrl === undefined) {
31
- throw new Error("URL could not be resolved");
32
- }
33
-
34
- const request: IRequest = {
35
- headers: {
36
- [LoaderHeader.cache]: false,
37
- [LoaderHeader.clientDetails]: {
38
- capabilities: { interactive: false },
39
- type: summarizerClientType,
40
- },
41
- [DriverHeader.summarizingClient]: true,
42
- [LoaderHeader.reconnect]: false,
43
- [LoaderHeader.version]: summaryVersion,
44
- },
45
- url: absoluteUrl,
46
- };
47
- const summarizerContainer = await loader.resolve(request);
48
- await waitForContainerConnection(summarizerContainer);
49
-
50
- const fluidObject =
51
- await requestFluidObject<FluidObject<ISummarizer>>(summarizerContainer, { url: "_summarizer" });
52
- if (fluidObject.ISummarizer === undefined) {
53
- throw new Error("Fluid object does not implement ISummarizer");
54
- }
55
-
56
- return {
57
- container: summarizerContainer,
58
- summarizer: fluidObject.ISummarizer,
59
- };
31
+ async function createSummarizerCore(
32
+ absoluteUrl: string | undefined,
33
+ loader: IHostLoader,
34
+ summaryVersion?: string,
35
+ ) {
36
+ if (absoluteUrl === undefined) {
37
+ throw new Error("URL could not be resolved");
38
+ }
39
+
40
+ const request: IRequest = {
41
+ headers: {
42
+ [LoaderHeader.cache]: false,
43
+ [LoaderHeader.clientDetails]: {
44
+ capabilities: { interactive: false },
45
+ type: summarizerClientType,
46
+ },
47
+ [DriverHeader.summarizingClient]: true,
48
+ [LoaderHeader.version]: summaryVersion,
49
+ },
50
+ url: absoluteUrl,
51
+ };
52
+ const summarizerContainer = await loader.resolve(request);
53
+ await waitForContainerConnection(summarizerContainer);
54
+
55
+ const fluidObject = await requestFluidObject<FluidObject<ISummarizer>>(summarizerContainer, {
56
+ url: "_summarizer",
57
+ });
58
+ if (fluidObject.ISummarizer === undefined) {
59
+ throw new Error("Fluid object does not implement ISummarizer");
60
+ }
61
+
62
+ return {
63
+ container: summarizerContainer,
64
+ summarizer: fluidObject.ISummarizer,
65
+ };
60
66
  }
61
67
 
62
68
  const defaultSummaryOptions: ISummaryRuntimeOptions = {
63
- summaryConfigOverrides: {
64
- state: "disableHeuristics",
65
- maxAckWaitTime: 10000,
66
- maxOpsSinceLastSummary: 7000,
67
- initialSummarizerDelayMs: 0,
68
- summarizerClientElection: false,
69
- },
69
+ summaryConfigOverrides: {
70
+ state: "disableHeuristics",
71
+ maxAckWaitTime: 10000,
72
+ maxOpsSinceLastSummary: 7000,
73
+ initialSummarizerDelayMs: 0,
74
+ },
70
75
  };
71
76
 
72
77
  export async function createSummarizerFromFactory(
73
- provider: ITestObjectProvider,
74
- container: IContainer,
75
- dataStoreFactory: IFluidDataStoreFactory,
76
- summaryVersion?: string,
77
- containerRuntimeFactoryType = ContainerRuntimeFactoryWithDefaultDataStore,
78
- registryEntries?: NamedFluidDataStoreRegistryEntries,
78
+ provider: ITestObjectProvider,
79
+ container: IContainer,
80
+ dataStoreFactory: IFluidDataStoreFactory,
81
+ summaryVersion?: string,
82
+ containerRuntimeFactoryType = ContainerRuntimeFactoryWithDefaultDataStore,
83
+ registryEntries?: NamedFluidDataStoreRegistryEntries,
79
84
  ): Promise<ISummarizer> {
80
- const innerRequestHandler = async (request: IRequest, runtime: IContainerRuntimeBase) =>
81
- runtime.IFluidHandleContext.resolveHandle(request);
82
- const runtimeFactory = new containerRuntimeFactoryType(
83
- dataStoreFactory,
84
- registryEntries ??
85
- [
86
- [dataStoreFactory.type, Promise.resolve(dataStoreFactory)],
87
- ],
88
- undefined,
89
- [innerRequestHandler],
90
- { summaryOptions: defaultSummaryOptions },
91
- );
92
-
93
- const loader = provider.createLoader(
94
- [[provider.defaultCodeDetails, runtimeFactory]],
95
- { configProvider: mockConfigProvider() },
96
- );
97
- const absoluteUrl = await container.getAbsoluteUrl("");
98
- return (await createSummarizerCore(absoluteUrl, loader, summaryVersion)).summarizer;
85
+ const innerRequestHandler = async (request: IRequest, runtime: IContainerRuntimeBase) =>
86
+ runtime.IFluidHandleContext.resolveHandle(request);
87
+ const runtimeFactory = new containerRuntimeFactoryType(
88
+ dataStoreFactory,
89
+ registryEntries ?? [[dataStoreFactory.type, Promise.resolve(dataStoreFactory)]],
90
+ undefined,
91
+ [innerRequestHandler],
92
+ { summaryOptions: defaultSummaryOptions },
93
+ );
94
+
95
+ const loader = provider.createLoader([[provider.defaultCodeDetails, runtimeFactory]], {
96
+ configProvider: mockConfigProvider(),
97
+ });
98
+ const absoluteUrl = await container.getAbsoluteUrl("");
99
+ return (await createSummarizerCore(absoluteUrl, loader, summaryVersion)).summarizer;
99
100
  }
100
101
 
101
102
  export async function createSummarizer(
102
- provider: ITestObjectProvider,
103
- container: IContainer,
104
- summaryVersion?: string,
105
- gcOptions?: IGCRuntimeOptions,
106
- configProvider: IConfigProviderBase = mockConfigProvider(),
103
+ provider: ITestObjectProvider,
104
+ container: IContainer,
105
+ summaryVersion?: string,
106
+ gcOptions?: IGCRuntimeOptions,
107
+ configProvider: IConfigProviderBase = mockConfigProvider(),
108
+ logger?: ITelemetryBaseLogger,
107
109
  ): Promise<ISummarizer> {
108
- const absoluteUrl = await container.getAbsoluteUrl("");
109
- return (await createSummarizerWithContainer(
110
- provider,
111
- absoluteUrl,
112
- summaryVersion,
113
- gcOptions,
114
- configProvider,
115
- )).summarizer;
110
+ const absoluteUrl = await container.getAbsoluteUrl("");
111
+ return (
112
+ await createSummarizerWithContainer(
113
+ provider,
114
+ absoluteUrl,
115
+ summaryVersion,
116
+ gcOptions,
117
+ configProvider,
118
+ logger,
119
+ )
120
+ ).summarizer;
116
121
  }
117
122
 
118
123
  export async function createSummarizerWithContainer(
119
- provider: ITestObjectProvider,
120
- absoluteUrl: string | undefined,
121
- summaryVersion?: string,
122
- gcOptions?: IGCRuntimeOptions,
123
- configProvider: IConfigProviderBase = mockConfigProvider(),
124
- ): Promise<{ container: IContainer; summarizer: ISummarizer; }> {
125
- const testContainerConfig: ITestContainerConfig = {
126
- runtimeOptions: {
127
- summaryOptions: defaultSummaryOptions,
128
- gcOptions,
129
- },
130
- loaderProps: { configProvider },
131
- };
132
- const loader = provider.makeTestLoader(testContainerConfig);
133
- return createSummarizerCore(absoluteUrl, loader, summaryVersion);
124
+ provider: ITestObjectProvider,
125
+ absoluteUrl: string | undefined,
126
+ summaryVersion?: string,
127
+ gcOptions?: IGCRuntimeOptions,
128
+ configProvider: IConfigProviderBase = mockConfigProvider(),
129
+ logger?: ITelemetryBaseLogger,
130
+ ): Promise<{ container: IContainer; summarizer: ISummarizer }> {
131
+ const testContainerConfig: ITestContainerConfig = {
132
+ runtimeOptions: {
133
+ summaryOptions: defaultSummaryOptions,
134
+ gcOptions,
135
+ },
136
+ loaderProps: { configProvider, logger },
137
+ };
138
+ const loader = provider.makeTestLoader(testContainerConfig);
139
+ return createSummarizerCore(absoluteUrl, loader, summaryVersion);
134
140
  }
135
-
141
+ /**
142
+ * Summarizes on demand and returns the summary tree, the version number and the reference sequence number of the
143
+ * submitted summary.
144
+ */
136
145
  export async function summarizeNow(summarizer: ISummarizer, reason: string = "end-to-end test") {
137
- const result = summarizer.summarizeOnDemand({ reason });
146
+ const result = summarizer.summarizeOnDemand({ reason });
138
147
 
139
- const submitResult = await result.summarySubmitted;
140
- assert(submitResult.success, "on-demand summary should submit");
141
- assert(submitResult.data.stage === "submit",
142
- "on-demand summary submitted data stage should be submit");
143
- assert(submitResult.data.summaryTree !== undefined, "summary tree should exist");
148
+ const submitResult = await timeoutAwait(result.summarySubmitted);
149
+ assert(submitResult.success, "on-demand summary should submit");
150
+ assert(
151
+ submitResult.data.stage === "submit",
152
+ "on-demand summary submitted data stage should be submit",
153
+ );
154
+ assert(submitResult.data.summaryTree !== undefined, "summary tree should exist");
144
155
 
145
- const broadcastResult = await result.summaryOpBroadcasted;
146
- assert(broadcastResult.success, "summary op should be broadcast");
156
+ const broadcastResult = await timeoutAwait(result.summaryOpBroadcasted);
157
+ assert(broadcastResult.success, "summary op should be broadcast");
147
158
 
148
- const ackNackResult = await result.receivedSummaryAckOrNack;
149
- assert(ackNackResult.success, "summary op should be acked");
159
+ const ackNackResult = await timeoutAwait(result.receivedSummaryAckOrNack);
160
+ assert(ackNackResult.success, "summary op should be acked");
150
161
 
151
- await new Promise((resolve) => process.nextTick(resolve));
152
-
153
- return {
154
- summaryTree: submitResult.data.summaryTree,
155
- summaryVersion: ackNackResult.data.summaryAckOp.contents.handle,
156
- };
157
- }
162
+ await new Promise((resolve) => process.nextTick(resolve));
158
163
 
159
- export async function waitForContainerConnection(container: IContainer): Promise<void> {
160
- if (container.connectionState !== ConnectionState.Connected) {
161
- return new Promise((resolve) => container.once("connected", () => resolve()));
162
- }
164
+ return {
165
+ summaryTree: submitResult.data.summaryTree,
166
+ summaryVersion: ackNackResult.data.summaryAckOp.contents.handle,
167
+ summaryRefSeq: submitResult.data.referenceSequenceNumber,
168
+ };
163
169
  }
@@ -0,0 +1,61 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { IContainer } from "@fluidframework/container-definitions";
7
+ import { ConnectionState, Container } from "@fluidframework/container-loader";
8
+ import { PromiseExecutor, timeoutPromise, TimeoutWithError } from "./timeoutUtils";
9
+
10
+ /**
11
+ * Waits for the specified container to emit a 'connected' event.
12
+ *
13
+ * @deprecated Use waitForContainerConnection instead.
14
+ * Note that an upcoming release will change the default parameters on that function to:
15
+ * - failOnContainerClose = true
16
+ * - timeoutOptions.durationMs = 1s
17
+ */
18
+ export async function ensureContainerConnected(container: Container): Promise<void> {
19
+ if (!container.connected) {
20
+ return timeoutPromise((resolve) => container.once("connected", () => resolve()));
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Utility function to wait for the specified Container to be in Connected state.
26
+ * If the Container is already connected, the Promise returns immediately; otherwise it resolves when the Container emits
27
+ * its 'connected' event.
28
+ * If failOnContainerClose === true, the returned Promise will be rejected if the container emits a 'closed' event
29
+ * before a 'connected' event.
30
+ * @param container - The container to wait for.
31
+ * @param failOnContainerClose - If true, the returned Promise will be rejected if the container emits a 'closed' event
32
+ * before a 'connected' event.
33
+ * Defaults to false (but this will change in an upcoming version).
34
+ * @param timeoutOptions - Options related to the behavior of the timeout.
35
+ * If not provided, no timeout will be applied and the promise will wait indefinitely for the Container to emit its
36
+ * 'connected' (or 'closed, if failOnContainerClose === true) event.
37
+ * @returns A Promise that resolves when the specified container emits a 'connected' event (or immediately if the
38
+ * Container is already connected).
39
+ * If failOnContainerClose === true and the container emits a 'closed' event before a 'connected' event, the Promise
40
+ * is rejected with the error from the 'closed' event, if any.
41
+ * If timeoutOptions is provided, the Promise will reject if the container hasn't emmited a relevant event before
42
+ * timeoutOptions.durationMs (which defaults to 250ms if left undefined).
43
+ */
44
+ export async function waitForContainerConnection(
45
+ container: IContainer,
46
+ failOnContainerClose: boolean = false,
47
+ timeoutOptions?: TimeoutWithError,
48
+ ): Promise<void> {
49
+ if (container.connectionState !== ConnectionState.Connected) {
50
+ const executor: PromiseExecutor = (resolve, reject) => {
51
+ container.once("connected", () => resolve());
52
+ if (failOnContainerClose) {
53
+ container.once("closed", (error) => reject(error));
54
+ }
55
+ };
56
+
57
+ return timeoutOptions === undefined
58
+ ? new Promise(executor)
59
+ : timeoutPromise(executor, timeoutOptions);
60
+ }
61
+ }