@fluidframework/test-utils 1.4.0-115997 → 2.0.0-dev-rc.1.0.0.224419
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/.eslintrc.js +8 -7
- package/.mocharc.js +12 -0
- package/CHANGELOG.md +177 -0
- package/README.md +44 -14
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/test-utils.api.md +395 -0
- package/dist/DriverWrappers.d.ts +32 -0
- package/dist/DriverWrappers.d.ts.map +1 -0
- package/dist/DriverWrappers.js +57 -0
- package/dist/DriverWrappers.js.map +1 -0
- package/dist/TestConfigs.d.ts +10 -0
- package/dist/TestConfigs.d.ts.map +1 -0
- package/dist/TestConfigs.js +17 -0
- package/dist/TestConfigs.js.map +1 -0
- package/dist/TestSummaryUtils.d.ts +61 -0
- package/dist/TestSummaryUtils.d.ts.map +1 -0
- package/dist/TestSummaryUtils.js +135 -0
- package/dist/TestSummaryUtils.js.map +1 -0
- package/dist/containerUtils.d.ts +46 -0
- package/dist/containerUtils.d.ts.map +1 -0
- package/dist/containerUtils.js +85 -0
- package/dist/containerUtils.js.map +1 -0
- package/dist/index.d.ts +14 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -19
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +6 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/loaderContainerTracker.d.ts +39 -15
- package/dist/loaderContainerTracker.d.ts.map +1 -1
- package/dist/loaderContainerTracker.js +214 -86
- package/dist/loaderContainerTracker.js.map +1 -1
- package/dist/localCodeLoader.d.ts +9 -2
- package/dist/localCodeLoader.d.ts.map +1 -1
- package/dist/localCodeLoader.js +18 -17
- package/dist/localCodeLoader.js.map +1 -1
- package/dist/localLoader.d.ts +3 -2
- package/dist/localLoader.d.ts.map +1 -1
- package/dist/localLoader.js +2 -0
- package/dist/localLoader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/retry.d.ts +1 -0
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +3 -2
- package/dist/retry.js.map +1 -1
- package/dist/test-utils-alpha.d.ts +305 -0
- package/dist/test-utils-beta.d.ts +202 -0
- package/dist/test-utils-public.d.ts +202 -0
- package/dist/test-utils-untrimmed.d.ts +1001 -0
- package/dist/testContainerRuntimeFactory.d.ts +4 -2
- package/dist/testContainerRuntimeFactory.d.ts.map +1 -1
- package/dist/testContainerRuntimeFactory.js +67 -12
- package/dist/testContainerRuntimeFactory.js.map +1 -1
- package/dist/testContainerRuntimeFactoryWithDefaultDataStore.d.ts +23 -0
- package/dist/testContainerRuntimeFactoryWithDefaultDataStore.d.ts.map +1 -0
- package/dist/testContainerRuntimeFactoryWithDefaultDataStore.js +32 -0
- package/dist/testContainerRuntimeFactoryWithDefaultDataStore.js.map +1 -0
- package/dist/testFluidObject.d.ts +30 -12
- package/dist/testFluidObject.d.ts.map +1 -1
- package/dist/testFluidObject.js +74 -43
- package/dist/testFluidObject.js.map +1 -1
- package/dist/testObjectProvider.d.ts +298 -41
- package/dist/testObjectProvider.d.ts.map +1 -1
- package/dist/testObjectProvider.js +377 -61
- package/dist/testObjectProvider.js.map +1 -1
- package/dist/timeoutUtils.d.ts +42 -0
- package/dist/timeoutUtils.d.ts.map +1 -1
- package/dist/timeoutUtils.js +142 -9
- package/dist/timeoutUtils.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/lib/test-utils-alpha.d.ts +305 -0
- package/lib/test-utils-beta.d.ts +202 -0
- package/lib/test-utils-public.d.ts +202 -0
- package/lib/test-utils-untrimmed.d.ts +1001 -0
- package/package.json +106 -72
- package/prettier.config.cjs +8 -0
- package/src/DriverWrappers.ts +83 -0
- package/src/TestConfigs.ts +17 -0
- package/src/TestSummaryUtils.ts +210 -0
- package/src/containerUtils.ts +90 -0
- package/src/index.ts +47 -9
- package/src/interfaces.ts +16 -7
- package/src/loaderContainerTracker.ts +754 -534
- package/src/localCodeLoader.ts +87 -86
- package/src/localLoader.ts +27 -26
- package/src/packageVersion.ts +1 -1
- package/src/retry.ts +33 -26
- package/src/testContainerRuntimeFactory.ts +152 -59
- package/src/testContainerRuntimeFactoryWithDefaultDataStore.ts +61 -0
- package/src/testFluidObject.ts +190 -147
- package/src/testObjectProvider.ts +931 -372
- package/src/timeoutUtils.ts +226 -37
- package/tsconfig.json +9 -12
package/src/timeoutUtils.ts
CHANGED
|
@@ -3,53 +3,242 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { assert, Deferred } from "@fluidframework/core-utils";
|
|
7
|
+
|
|
8
|
+
// @deprecated this value is no longer used
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
6
12
|
export const defaultTimeoutDurationMs = 250;
|
|
7
13
|
|
|
14
|
+
const timeBuffer = 15; // leave 15 ms leeway for finish processing
|
|
15
|
+
|
|
16
|
+
// TestTimeout class that manages tracking of test timeout. It creates a timer when timeout is in effect,
|
|
17
|
+
// and provides a promise that will be rejected some time (as defined by `timeBuffer`) before the test timeout happens.
|
|
18
|
+
// This will ensure that async awaits in tests do not end up timing out the tests but resolve / reject
|
|
19
|
+
// before that happens.
|
|
20
|
+
// Once rejected, a new TestTimeout object will be create for the timeout.
|
|
21
|
+
class TestTimeout {
|
|
22
|
+
private timeout: number = 0;
|
|
23
|
+
private timer: NodeJS.Timeout | undefined;
|
|
24
|
+
private readonly deferred: Deferred<void>;
|
|
25
|
+
private rejected = false;
|
|
26
|
+
|
|
27
|
+
private static instance: TestTimeout = new TestTimeout();
|
|
28
|
+
public static reset(runnable: Mocha.Runnable) {
|
|
29
|
+
TestTimeout.clear();
|
|
30
|
+
TestTimeout.instance.resetTimer(runnable);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public static clear() {
|
|
34
|
+
if (TestTimeout.instance.rejected) {
|
|
35
|
+
TestTimeout.instance = new TestTimeout();
|
|
36
|
+
} else {
|
|
37
|
+
TestTimeout.instance.clearTimer();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public static getInstance() {
|
|
42
|
+
return TestTimeout.instance;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public async getPromise() {
|
|
46
|
+
return this.deferred.promise;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public getTimeout() {
|
|
50
|
+
return this.timeout;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private constructor() {
|
|
54
|
+
this.deferred = new Deferred();
|
|
55
|
+
// Ignore rejection for timeout promise if no one is waiting for it.
|
|
56
|
+
this.deferred.promise.catch(() => {});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private resetTimer(runnable: Mocha.Runnable) {
|
|
60
|
+
assert(!this.timer, "clearTimer should have been called before reset");
|
|
61
|
+
assert(!this.deferred.isCompleted, "can't reset a completed TestTimeout");
|
|
62
|
+
|
|
63
|
+
// Check the test timeout setting
|
|
64
|
+
const timeout = runnable.timeout();
|
|
65
|
+
if (!(Number.isFinite(timeout) && timeout > 0)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// subtract a buffer
|
|
70
|
+
this.timeout = Math.max(timeout - timeBuffer, 1);
|
|
71
|
+
|
|
72
|
+
// Set up timer to reject near the test timeout.
|
|
73
|
+
this.timer = setTimeout(() => {
|
|
74
|
+
this.deferred.reject(this);
|
|
75
|
+
this.rejected = true;
|
|
76
|
+
}, this.timeout);
|
|
77
|
+
}
|
|
78
|
+
private clearTimer() {
|
|
79
|
+
if (this.timer) {
|
|
80
|
+
clearTimeout(this.timer);
|
|
81
|
+
this.timer = undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// only register if we are running with mocha-test-setup loaded
|
|
87
|
+
if (globalThis.getMochaModule !== undefined) {
|
|
88
|
+
// patching resetTimeout and clearTimeout on the runnable object
|
|
89
|
+
// so we can track when test timeout are enforced
|
|
90
|
+
const mochaModule = globalThis.getMochaModule() as typeof Mocha;
|
|
91
|
+
const runnablePrototype = mochaModule.Runnable.prototype;
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
93
|
+
const oldResetTimeoutFunc = runnablePrototype.resetTimeout;
|
|
94
|
+
runnablePrototype.resetTimeout = function (this: Mocha.Runnable) {
|
|
95
|
+
oldResetTimeoutFunc.call(this);
|
|
96
|
+
TestTimeout.reset(this);
|
|
97
|
+
};
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
99
|
+
const oldClearTimeoutFunc = runnablePrototype.clearTimeout;
|
|
100
|
+
runnablePrototype.clearTimeout = function (this: Mocha.Runnable) {
|
|
101
|
+
TestTimeout.clear();
|
|
102
|
+
oldClearTimeoutFunc.call(this);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @internal
|
|
108
|
+
*/
|
|
8
109
|
export interface TimeoutWithError {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Timeout duration in milliseconds, if it is great than 0 and not Infinity
|
|
112
|
+
* If it is undefined, then it will use test timeout if we are in side the test function
|
|
113
|
+
* Otherwise, there is no timeout
|
|
114
|
+
*/
|
|
115
|
+
durationMs?: number;
|
|
116
|
+
reject?: true;
|
|
117
|
+
errorMsg?: string;
|
|
12
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* @internal
|
|
121
|
+
*/
|
|
13
122
|
export interface TimeoutWithValue<T = void> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Timeout duration in milliseconds, if it is great than 0 and not Infinity
|
|
125
|
+
* If it is undefined, then it will use test timeout if we are in side the test function
|
|
126
|
+
* Otherwise, there is no timeout
|
|
127
|
+
*/
|
|
128
|
+
durationMs?: number;
|
|
129
|
+
reject: false;
|
|
130
|
+
value: T;
|
|
17
131
|
}
|
|
18
132
|
|
|
133
|
+
export type PromiseExecutor<T = void> = (
|
|
134
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
135
|
+
reject: (reason?: any) => void,
|
|
136
|
+
) => void;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Wraps the given promise around with promise that will complete after a specific timeout if the original promise does
|
|
140
|
+
* not resolve by then. By default, it uses the mocha test timeout and complete the promise just before that so that
|
|
141
|
+
* tests don't time out because of unpredictable awaits.
|
|
142
|
+
* The timeout can be overridden via timeoutOptions but it's recommended to use the default value.
|
|
143
|
+
* @param promise - The promise to be awaited.
|
|
144
|
+
* @param timeoutOptions - Options that can be used to override the timeout and / or define the behavior
|
|
145
|
+
* when the promise is not fulfilled. For example, instead of rejecting the promise, resolve with a
|
|
146
|
+
* specific value.
|
|
147
|
+
* @returns A new promise that will complete when the given promise resolves or the timeout expires.
|
|
148
|
+
* @internal
|
|
149
|
+
*/
|
|
19
150
|
export async function timeoutAwait<T = void>(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
) {
|
|
23
|
-
|
|
151
|
+
promise: PromiseLike<T>,
|
|
152
|
+
timeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},
|
|
153
|
+
): Promise<T> {
|
|
154
|
+
return Promise.race([promise, timeoutPromise<T>(() => {}, timeoutOptions)]);
|
|
24
155
|
}
|
|
25
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Creates a promise from the given executor that will complete after a specific timeout. By default, it uses the mocha
|
|
159
|
+
* test timeout and complete the promise just before that so that tests don't time out because of unpredictable awaits.
|
|
160
|
+
* The timeout can be overridden via timeoutOptions but it's recommended to use the default value.
|
|
161
|
+
* @param executor - The executor for the promise.
|
|
162
|
+
* @param timeoutOptions - Options that can be used to override the timeout and / or define the behavior when
|
|
163
|
+
* the promise is not fulfilled. For example, instead of rejecting the promise, resolve with a specific value.
|
|
164
|
+
* @returns A new promise that will complete when the given executor resolves or the timeout expires.
|
|
165
|
+
* @internal
|
|
166
|
+
*/
|
|
26
167
|
export async function timeoutPromise<T = void>(
|
|
27
|
-
|
|
28
|
-
|
|
168
|
+
executor: (
|
|
169
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
170
|
+
reject: (reason?: any) => void,
|
|
171
|
+
) => void,
|
|
172
|
+
timeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},
|
|
29
173
|
): Promise<T> {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
174
|
+
// create the timeout error outside the async task, so its callstack includes
|
|
175
|
+
// the original call site, this makes it easier to debug
|
|
176
|
+
const err =
|
|
177
|
+
timeoutOptions.reject === false
|
|
178
|
+
? undefined
|
|
179
|
+
: new Error(timeoutOptions.errorMsg ?? "Timed out");
|
|
180
|
+
const executorPromise = getTimeoutPromise(executor, timeoutOptions, err);
|
|
181
|
+
|
|
182
|
+
const currentTestTimeout = TestTimeout.getInstance();
|
|
183
|
+
if (currentTestTimeout === undefined) {
|
|
184
|
+
return executorPromise;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return Promise.race([executorPromise, currentTestTimeout.getPromise()]).catch((e) => {
|
|
188
|
+
if (e === currentTestTimeout) {
|
|
189
|
+
if (timeoutOptions.reject !== false) {
|
|
190
|
+
// If the rejection is because of the timeout then
|
|
191
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
192
|
+
const errorObject = err!;
|
|
193
|
+
errorObject.message = `${
|
|
194
|
+
timeoutOptions.errorMsg ?? "Test timed out"
|
|
195
|
+
} (${currentTestTimeout.getTimeout()}ms)`;
|
|
196
|
+
throw errorObject;
|
|
197
|
+
}
|
|
198
|
+
return timeoutOptions.value;
|
|
199
|
+
}
|
|
200
|
+
throw e;
|
|
201
|
+
}) as Promise<T>;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Create a promise based on the timeout options
|
|
205
|
+
async function getTimeoutPromise<T = void>(
|
|
206
|
+
executor: (
|
|
207
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
208
|
+
reject: (reason?: any) => void,
|
|
209
|
+
) => void,
|
|
210
|
+
timeoutOptions: TimeoutWithError | TimeoutWithValue<T>,
|
|
211
|
+
err: Error | undefined,
|
|
212
|
+
) {
|
|
213
|
+
const timeout = timeoutOptions.durationMs ?? 0;
|
|
214
|
+
if (timeout <= 0 || !Number.isFinite(timeout)) {
|
|
215
|
+
return new Promise(executor);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return new Promise<T>((resolve, reject) => {
|
|
219
|
+
const timeoutRejections = () => {
|
|
220
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
221
|
+
const errorObject = err!;
|
|
222
|
+
errorObject.message = `${errorObject.message} (${timeout}ms)`;
|
|
223
|
+
reject(err);
|
|
224
|
+
};
|
|
225
|
+
const timer = setTimeout(
|
|
226
|
+
() =>
|
|
227
|
+
timeoutOptions.reject === false
|
|
228
|
+
? resolve(timeoutOptions.value)
|
|
229
|
+
: timeoutRejections(),
|
|
230
|
+
timeout,
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
executor(
|
|
234
|
+
(value) => {
|
|
235
|
+
clearTimeout(timer);
|
|
236
|
+
resolve(value);
|
|
237
|
+
},
|
|
238
|
+
(reason) => {
|
|
239
|
+
clearTimeout(timer);
|
|
240
|
+
reject(reason);
|
|
241
|
+
},
|
|
242
|
+
);
|
|
243
|
+
});
|
|
55
244
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"include": [
|
|
12
|
-
"src/**/*"
|
|
13
|
-
]
|
|
2
|
+
"extends": "@fluidframework/build-common/ts-common-config.json",
|
|
3
|
+
"exclude": ["src/test/**/*"],
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"composite": true,
|
|
8
|
+
"types": ["mocha", "node"],
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*"],
|
|
14
11
|
}
|