@ueu/ueu-canvas 0.0.2
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/LICENSE +21 -0
- package/html-loader.js +7 -0
- package/jest.config.js +20 -0
- package/package.json +45 -0
- package/src/Account.ts +69 -0
- package/src/NotImplementedException.ts +3 -0
- package/src/__mocks__/Account.ts +55 -0
- package/src/__mocks__/index.ts +8 -0
- package/src/__mocks__/mockAccountData.ts +43 -0
- package/src/__mocks__/mockEnrollment.ts +22 -0
- package/src/__mocks__/mockRubricData.ts +71 -0
- package/src/__mocks__/mockTabData.ts +13 -0
- package/src/__mocks__/mockTermData.ts +7 -0
- package/src/__mocks__/mockUserData.ts +14 -0
- package/src/__mocks__/utils.ts +9 -0
- package/src/__tests__/canvasUtils.test.ts +527 -0
- package/src/__tests__/fetch.test.ts +310 -0
- package/src/__tests__/profile.test.ts +19 -0
- package/src/__tests__/rubrics.test.ts +131 -0
- package/src/baseCanvasObject.ts +139 -0
- package/src/canvasDataDefs.ts +124 -0
- package/src/canvasUtils.ts +420 -0
- package/src/consts.ts +2 -0
- package/src/content/BaseContentItem.ts +219 -0
- package/src/content/ContentKind.ts +85 -0
- package/src/content/__mocks__/determineContent.ts +27 -0
- package/src/content/__mocks__/mockAssignmentSubmission.ts +37 -0
- package/src/content/__mocks__/mockContentData.ts +98 -0
- package/src/content/__mocks__/openThisContentInTarget.ts +2 -0
- package/src/content/__testingUtils__/utils.ts +47 -0
- package/src/content/__tests__/determineContent.test.ts +101 -0
- package/src/content/__tests__/getContentFuncs.test.ts +78 -0
- package/src/content/assignments/Assignment.ts +69 -0
- package/src/content/assignments/AssignmentKind.ts +29 -0
- package/src/content/assignments/__tests__/AssignmentKind.test.ts +31 -0
- package/src/content/assignments/__tests__/assignments.test.ts +72 -0
- package/src/content/assignments/deleteAssignmentGroup.ts +15 -0
- package/src/content/assignments/genAssignmentGroups.ts +15 -0
- package/src/content/assignments/index.ts +31 -0
- package/src/content/assignments/legacy.ts +11 -0
- package/src/content/assignments/updateAssignmentDueDates.ts +25 -0
- package/src/content/determineContent.ts +72 -0
- package/src/content/discussions/Discussion.ts +32 -0
- package/src/content/discussions/DiscussionKind.ts +30 -0
- package/src/content/discussions/__tests__/DiscussionKind.test.ts +31 -0
- package/src/content/discussions/genEquivalentDiscussionData.ts +97 -0
- package/src/content/discussions/index.ts +2 -0
- package/src/content/getContentFuncs.ts +30 -0
- package/src/content/index.ts +11 -0
- package/src/content/openThisContentInTarget.ts +38 -0
- package/src/content/pages/Page.ts +35 -0
- package/src/content/pages/PageKind.ts +39 -0
- package/src/content/pages/__tests__/PageKind.test.ts +86 -0
- package/src/content/pages/__tests__/learningMaterialsForModule.ts +57 -0
- package/src/content/pages/index.ts +12 -0
- package/src/content/pages/learningMaterialsForModule.ts +16 -0
- package/src/content/pages/types.ts +11 -0
- package/src/content/quizzes/Quiz.ts +25 -0
- package/src/content/quizzes/QuizKind.ts +23 -0
- package/src/content/quizzes/__tests__/QuizKind.test.ts +30 -0
- package/src/content/quizzes/index.ts +3 -0
- package/src/content/quizzes/types.ts +101 -0
- package/src/content/types.ts +302 -0
- package/src/course/Course.ts +538 -0
- package/src/course/IBlueprintCourse.ts +8 -0
- package/src/course/__mocks__/blueprint.ts +31 -0
- package/src/course/__mocks__/mockCourseData.ts +49 -0
- package/src/course/__mocks__/mockLatePolicy.ts +14 -0
- package/src/course/__mocks__/mockModuleData.ts +60 -0
- package/src/course/__mocks__/mockProgressData.ts +28 -0
- package/src/course/__mocks__/syllabus.gallant.html +302 -0
- package/src/course/__mocks__/syllabus.goofus.html +288 -0
- package/src/course/__mocks__/syllabus.grad.html +282 -0
- package/src/course/__tests__/Course.test.ts +91 -0
- package/src/course/__tests__/blueprint.test.ts +292 -0
- package/src/course/__tests__/changeStartDate.test.ts +180 -0
- package/src/course/__tests__/index.test.ts +104 -0
- package/src/course/blueprint.ts +116 -0
- package/src/course/cachedGetAssociatedCoursesFunc.ts +13 -0
- package/src/course/changeStartDate.ts +139 -0
- package/src/course/code.ts +32 -0
- package/src/course/courseTypes.ts +161 -0
- package/src/course/getCourseIdFromUrl.ts +7 -0
- package/src/course/getSections.ts +9 -0
- package/src/course/getTermNameFromSections.ts +9 -0
- package/src/course/index.ts +12 -0
- package/src/course/migration/__mocks__/mockMigrationData.ts +39 -0
- package/src/course/migration/__tests__/migration.test.ts +173 -0
- package/src/course/migration/__tests__/migrationCache.test.ts +45 -0
- package/src/course/migration/index.ts +134 -0
- package/src/course/migration/migrationCache.ts +56 -0
- package/src/course/modules.ts +113 -0
- package/src/course/notABlueprintError.ts +3 -0
- package/src/course/references/getReferencesForText.test.ts +18 -0
- package/src/course/references/getReferencesForText.ts +12 -0
- package/src/course/references/getReferencesTemplate.ts +28 -0
- package/src/course/references/mockCiteAsResponse.ts +320 -0
- package/src/course/retireBlueprint.ts +21 -0
- package/src/course/toolbox.ts +107 -0
- package/src/date.ts +119 -0
- package/src/enrollments/EnrollmentTypes.ts +76 -0
- package/src/enrollments/enrollmentRole.ts +6 -0
- package/src/enrollments/getEnrollmentGenerator.ts +79 -0
- package/src/enrollments/index.ts +3 -0
- package/src/fetch/__mocks__/fetchJson.ts +1 -0
- package/src/fetch/__mocks__/getPagedDataGenerator.ts +8 -0
- package/src/fetch/__mocks__/index.ts +6 -0
- package/src/fetch/apiGetConfig.ts +10 -0
- package/src/fetch/apiWriteConfig.ts +13 -0
- package/src/fetch/canvasDataFetchGenFunc.ts +20 -0
- package/src/fetch/fetchJson.ts +22 -0
- package/src/fetch/getPagedDataGenerator.ts +157 -0
- package/src/fetch/index.ts +5 -0
- package/src/fetch/utils.ts +16 -0
- package/src/files.ts +60 -0
- package/src/fixes/annotations.ts +105 -0
- package/src/fixes/index.ts +81 -0
- package/src/index.ts +29 -0
- package/src/profile.ts +253 -0
- package/src/rubricTypes.ts +82 -0
- package/src/rubrics.ts +48 -0
- package/src/term/Term.ts +77 -0
- package/src/term/__tests__/dateFromTermName.test.ts +22 -0
- package/src/term/__tests__/getTermsGenerator.test.ts +24 -0
- package/src/term/dateFromTermName.ts +17 -0
- package/src/term/getTermsGenerator.ts +21 -0
- package/src/term/index.ts +3 -0
- package/src/toolbox.ts +19 -0
- package/src/types.ts +134 -0
- package/src/users/getUserGenerator.ts +39 -0
- package/src/users/index.ts +1 -0
- package/tsconfig.json +46 -0
- package/webpack.config.js +122 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import {
|
|
2
|
+
callAll,
|
|
3
|
+
range,
|
|
4
|
+
parentElement,
|
|
5
|
+
formDataify,
|
|
6
|
+
queryStringify,
|
|
7
|
+
batchify,
|
|
8
|
+
deFormDataify, deepObjectMerge, generatorMap, batchGen, renderAsyncGen
|
|
9
|
+
} from '../canvasUtils'
|
|
10
|
+
import {describe, expect} from "@jest/globals";
|
|
11
|
+
import assert from "assert";
|
|
12
|
+
import {AssertionError} from "node:assert";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const TEST_STRING = String.fromCharCode(...Array.from(range(32, 126)))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
describe('CallAll Tests', () => {
|
|
19
|
+
test('callAll returns the results of a list of paramless functions', () => {
|
|
20
|
+
|
|
21
|
+
const list = Array.from(TEST_STRING).map((char) => () => char)
|
|
22
|
+
expect(callAll(list).join('')).toMatch(TEST_STRING);
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('callAll returns the results of a list of function passed the same parameters', () => {
|
|
26
|
+
const list = Array.from(range(0, 100)).map((num) => {
|
|
27
|
+
return (value: number) => {
|
|
28
|
+
return num * value
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
expect(callAll(list, 2)).toStrictEqual(Array.from(range(0, 100)).map((num) => num * 2))
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('CallAll runs a list of function, parameter pairs and returns the results as a list.', () => {
|
|
36
|
+
const list = Array.from(range(0, 100)).map((value) => {
|
|
37
|
+
return {
|
|
38
|
+
func: (x: number) => x,
|
|
39
|
+
params: value
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
expect(callAll(list)).toStrictEqual(Array.from(range(0, 100)))
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('ParentElement traverses up parents of a DOMElement and returns the first parent matching a tag. Otherwise returns null', () => {
|
|
48
|
+
const elId = 'elementId'
|
|
49
|
+
const parentId = 'parentId'
|
|
50
|
+
const html = `<div id="${parentId}"><a><span id="${elId}">XXX</span></a></div>`;
|
|
51
|
+
const body = document.createElement('div');
|
|
52
|
+
body.innerHTML = html;
|
|
53
|
+
const element = body.querySelector(`#${elId}`) as Element | null;
|
|
54
|
+
const parent = parentElement(element, 'div');
|
|
55
|
+
expect(parent?.id).toBe(parentId);
|
|
56
|
+
expect(parentElement(element, 'blockquote')).toBe(null)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('FormDatify properly serializes objects', () => {
|
|
60
|
+
const testData = {
|
|
61
|
+
a: [1],
|
|
62
|
+
b: 'hello!',
|
|
63
|
+
c: {
|
|
64
|
+
c1: [1],
|
|
65
|
+
c2: 2
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const formD = formDataify(testData);
|
|
70
|
+
const entries = [...formD.entries()];
|
|
71
|
+
expect(entries[0]).toStrictEqual(['a[]', '1'])
|
|
72
|
+
expect(entries[1]).toStrictEqual(['b', 'hello!'])
|
|
73
|
+
expect(entries[2]).toStrictEqual(['c[c1][]', '1'])
|
|
74
|
+
expect(entries[3]).toStrictEqual(['c[c2]', '2'])
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('deFormDatify properly deserializes objects', () => {
|
|
78
|
+
const testData = {
|
|
79
|
+
a: [1],
|
|
80
|
+
b: 'hello!',
|
|
81
|
+
c: {
|
|
82
|
+
c1: [1],
|
|
83
|
+
c2: 2
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const formD = formDataify(testData);
|
|
88
|
+
const entries = [...formD.entries()];
|
|
89
|
+
const newData = deFormDataify(formD);
|
|
90
|
+
expect(newData).toEqual({
|
|
91
|
+
a: ['1'],
|
|
92
|
+
b: 'hello!',
|
|
93
|
+
c: {
|
|
94
|
+
c1: ['1'],
|
|
95
|
+
c2: '2'
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
test('Querystringify', () => {
|
|
102
|
+
const testData = {
|
|
103
|
+
a: [1, 2, 3],
|
|
104
|
+
b: 'hello!',
|
|
105
|
+
c: {
|
|
106
|
+
c1: [1],
|
|
107
|
+
c2: 2
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const data = queryStringify(testData);
|
|
112
|
+
const entries = Array.from(data);
|
|
113
|
+
expect(entries[0]).toStrictEqual(['a[]', '1'])
|
|
114
|
+
expect(entries[1]).toStrictEqual(['a[]', '2'])
|
|
115
|
+
expect(entries[2]).toStrictEqual(['a[]', '3'])
|
|
116
|
+
expect(entries[3]).toStrictEqual(['b', 'hello!'])
|
|
117
|
+
expect(entries[4]).toStrictEqual(['c[c1][]', '1'])
|
|
118
|
+
expect(entries[5]).toStrictEqual(['c[c2]', '2'])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('Batchify', () => {
|
|
122
|
+
expect(batchify([1, 2, 3, 4], 2)).toStrictEqual([[1, 2], [3, 4]])
|
|
123
|
+
expect(batchify([1, 2, 3, 4, 5], 2)).toStrictEqual([[1, 2], [3, 4], [5]])
|
|
124
|
+
expect(batchify([1, 2, 3, 4, 5], 5)).toStrictEqual([[1, 2, 3, 4, 5]])
|
|
125
|
+
expect(batchify([1, 2, 3, 4, 5], 6)).toStrictEqual([[1, 2, 3, 4, 5]])
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
describe("Recursive object merge", () => {
|
|
129
|
+
test('Non-indexing merges', () => {
|
|
130
|
+
expect(deepObjectMerge(1, null)).toBe(1)
|
|
131
|
+
expect(deepObjectMerge(undefined, 2)).toBe(2)
|
|
132
|
+
expect(deepObjectMerge(undefined, null)).toBe(null)
|
|
133
|
+
expect(deepObjectMerge(null, undefined)).toBe(null)
|
|
134
|
+
|
|
135
|
+
expect(() => deepObjectMerge<string | number>(1, 'apple', false)).toThrow('Type clash on merge number 1, string apple')
|
|
136
|
+
expect(() => deepObjectMerge<string | number>(1, '2', false)).toThrow('Type clash on merge number 1, string 2')
|
|
137
|
+
expect(() => deepObjectMerge(1, 2, false)).toThrow('Values unmergeable')
|
|
138
|
+
|
|
139
|
+
expect(deepObjectMerge<string | number>('apple', 1, true)).toBe('apple');
|
|
140
|
+
expect(deepObjectMerge<string | number>(1, '2', true)).toBe(1);
|
|
141
|
+
expect(deepObjectMerge(1, 2, true)).toBe(1)
|
|
142
|
+
expect(deepObjectMerge({
|
|
143
|
+
name: "todd"
|
|
144
|
+
}, {
|
|
145
|
+
name: 'steve'
|
|
146
|
+
}, true)?.name).toBe('todd');
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test('Arrays', () => {
|
|
150
|
+
expect(deepObjectMerge([1, 2], [3, 4])).toHaveLength(4);
|
|
151
|
+
expect(deepObjectMerge([1, 2], null)).toHaveLength(2);
|
|
152
|
+
expect(deepObjectMerge(null, [3, 4])).toHaveLength(2);
|
|
153
|
+
[1, 2, 3, 4].forEach(i => {
|
|
154
|
+
expect(deepObjectMerge([1, 2], [3, 4])).toContain(i);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const simpleMerged = deepObjectMerge([1, 2], ['a', 'b']);
|
|
158
|
+
[1, 2, 'a', 'b'].forEach(a => {
|
|
159
|
+
assert(simpleMerged);
|
|
160
|
+
expect(simpleMerged).toContain(a);
|
|
161
|
+
})
|
|
162
|
+
const dougsAndHenries = deepObjectMerge([{name: 'henry'}, {name: 'doug'}], [{name: 'henry'}, {name: 'doug'}]);
|
|
163
|
+
expect(dougsAndHenries).toHaveLength(4)
|
|
164
|
+
expect(dougsAndHenries?.map(entry => entry.name)).toEqual(['henry', 'doug', 'henry', 'doug'])
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('Files and Complex arrays', () => {
|
|
168
|
+
const object = {key: "X", value: 7, list: [1, 2, 3, 4, 5]};
|
|
169
|
+
const file = new File([JSON.stringify(object)], 'file.txt')
|
|
170
|
+
expect(deepObjectMerge(file, file)?.name).toBe(file.name);
|
|
171
|
+
let counterFile = new File([JSON.stringify(object)], 'file2.txt');
|
|
172
|
+
expect(() => deepObjectMerge(file, counterFile)?.name).toThrow(AssertionError)
|
|
173
|
+
counterFile = new File(['aaaaaa'], 'file.txt');
|
|
174
|
+
expect(() => deepObjectMerge(file, counterFile)?.name).toThrow(AssertionError)
|
|
175
|
+
expect(deepObjectMerge(file, counterFile, true)?.name).toBe(file.name)
|
|
176
|
+
const complexMerged = deepObjectMerge([null, 'a', file], [undefined, [1, 2, 3, 4], 5, object, object, file]);
|
|
177
|
+
expect(complexMerged).toHaveLength(9);
|
|
178
|
+
for (const value of [5, 'a', null, undefined]) {
|
|
179
|
+
expect(complexMerged).toContain(value);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const objectsInMerge = complexMerged?.filter(item => item && typeof item === 'object') ?? [];
|
|
183
|
+
expect(objectsInMerge).toHaveLength(5);
|
|
184
|
+
const [extractedObject, extractedObjectTwo] = objectsInMerge.filter(item => item && !(item instanceof File || Array.isArray(item)));
|
|
185
|
+
expect(extractedObject).toStrictEqual(object);
|
|
186
|
+
expect(extractedObject === extractedObjectTwo).toBe(false);
|
|
187
|
+
expect(extractedObject === object).toBe(false);
|
|
188
|
+
const [fileOne, fileTwo] = objectsInMerge.filter(item => item instanceof File);
|
|
189
|
+
expect(fileOne).toBe(fileTwo);
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('FormData', () => {
|
|
193
|
+
const formMerge = deepObjectMerge({data: new FormData()}, {body: new FormData()});
|
|
194
|
+
expect(formMerge?.body).toBeInstanceOf(FormData);
|
|
195
|
+
expect(formMerge?.data).toBeInstanceOf(FormData);
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('Objects', () => {
|
|
199
|
+
expect(() => deepObjectMerge({a: 1}, {a: "Dog"})).toThrow('Type clash on merge')
|
|
200
|
+
expect(deepObjectMerge({a: 1}, {b: 2})).toStrictEqual({a: 1, b: 2});
|
|
201
|
+
expect(deepObjectMerge({
|
|
202
|
+
list: [1, 2, 3]
|
|
203
|
+
}, {
|
|
204
|
+
list: [4, 5, 6]
|
|
205
|
+
})).toStrictEqual({list: [1, 2, 3, 4, 5, 6]})
|
|
206
|
+
expect(deepObjectMerge({
|
|
207
|
+
item: {
|
|
208
|
+
name: "NAME",
|
|
209
|
+
age: 35
|
|
210
|
+
}
|
|
211
|
+
}, {
|
|
212
|
+
item: {
|
|
213
|
+
height: 165,
|
|
214
|
+
children: ['bobby', 'andrew']
|
|
215
|
+
}
|
|
216
|
+
})).toStrictEqual({
|
|
217
|
+
item: {
|
|
218
|
+
name: "NAME",
|
|
219
|
+
age: 35,
|
|
220
|
+
height: 165,
|
|
221
|
+
children: ['bobby', 'andrew']
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('Self-containment error', () => {
|
|
227
|
+
|
|
228
|
+
let smith: { [key: string]: any } = {};
|
|
229
|
+
let smithsHouse: { [key: string]: any } = {};
|
|
230
|
+
|
|
231
|
+
smith = {
|
|
232
|
+
name: 'Smith',
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
smithsHouse = {
|
|
236
|
+
address: '224 West 9th',
|
|
237
|
+
owner: smith,
|
|
238
|
+
inhabitants: [smith]
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
smith['house'] = smithsHouse;
|
|
242
|
+
expect(() => {
|
|
243
|
+
deepObjectMerge(smith, {
|
|
244
|
+
dog: "Steven"
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
}).toThrow('Infinite Loop')
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
test('Complex Merge', () => {
|
|
251
|
+
|
|
252
|
+
const a: Record<string, any> = {
|
|
253
|
+
list: [1, 2],
|
|
254
|
+
human: {
|
|
255
|
+
name: "Stan",
|
|
256
|
+
age: 45,
|
|
257
|
+
children: ['rod', 'lucy'],
|
|
258
|
+
dog: {
|
|
259
|
+
name: "Walter"
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
},
|
|
264
|
+
cat: {
|
|
265
|
+
name: "Stephanie"
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const b: Record<string, any> = {
|
|
270
|
+
list: [2, 3, 4, undefined],
|
|
271
|
+
human: {
|
|
272
|
+
name: "Stan",
|
|
273
|
+
children: ['stacy'],
|
|
274
|
+
dog: {
|
|
275
|
+
age: 7,
|
|
276
|
+
},
|
|
277
|
+
fish: {
|
|
278
|
+
name: "Capn Guppy",
|
|
279
|
+
previousNames: ['Admiral Blub', "Mr. Bubbles"]
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const expectedMerge = {
|
|
286
|
+
list: [1, 2, 2, 3, 4, undefined],
|
|
287
|
+
human: {
|
|
288
|
+
name: "Stan",
|
|
289
|
+
age: 45,
|
|
290
|
+
children: ['rod', 'lucy', 'stacy'],
|
|
291
|
+
dog: {
|
|
292
|
+
name: "Walter",
|
|
293
|
+
age: 7
|
|
294
|
+
},
|
|
295
|
+
fish: {
|
|
296
|
+
name: 'Capn Guppy',
|
|
297
|
+
previousNames: ['Admiral Blub', "Mr. Bubbles"]
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
cat: {
|
|
301
|
+
name: "Stephanie"
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
expect(deepObjectMerge(a, b)).toEqual(expectedMerge);
|
|
308
|
+
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
describe('batchGen', () => {
|
|
317
|
+
async function* testGenerator() {
|
|
318
|
+
yield 1;
|
|
319
|
+
yield 2;
|
|
320
|
+
yield 3;
|
|
321
|
+
yield 4;
|
|
322
|
+
yield 5;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
it('should yield batches of specified size', async () => {
|
|
326
|
+
const generator = batchGen(testGenerator(), 2);
|
|
327
|
+
const result = [];
|
|
328
|
+
|
|
329
|
+
for await (const batch of generator) {
|
|
330
|
+
result.push(batch);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
expect(result).toEqual([[1, 2], [3, 4], [5]]);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('should yield all items in a single batch if batchSize is greater than total items', async () => {
|
|
337
|
+
const generator = batchGen(testGenerator(), 10);
|
|
338
|
+
const result = [];
|
|
339
|
+
|
|
340
|
+
for await (const batch of generator) {
|
|
341
|
+
result.push(batch);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
expect(result).toEqual([[1, 2, 3, 4, 5]]);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should handle empty generator', async () => {
|
|
348
|
+
async function* emptyGenerator() {}
|
|
349
|
+
const generator = batchGen(emptyGenerator(), 2);
|
|
350
|
+
const result = [];
|
|
351
|
+
|
|
352
|
+
for await (const batch of generator) {
|
|
353
|
+
result.push(batch);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
expect(result).toEqual([]);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
describe('renderAsyncGen', () => {
|
|
361
|
+
async function* testGenerator() {
|
|
362
|
+
yield 'a';
|
|
363
|
+
yield 'b';
|
|
364
|
+
yield 'c';
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
it('should collect all items from the generator', async () => {
|
|
368
|
+
const generator = testGenerator();
|
|
369
|
+
const result = await renderAsyncGen(generator);
|
|
370
|
+
|
|
371
|
+
expect(result).toEqual(['a', 'b', 'c']);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should handle an empty generator', async () => {
|
|
375
|
+
async function* emptyGenerator() {}
|
|
376
|
+
const generator = emptyGenerator();
|
|
377
|
+
const result = await renderAsyncGen(generator);
|
|
378
|
+
|
|
379
|
+
expect(result).toEqual([]);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('generatorMap', () => {
|
|
384
|
+
async function* testGenerator() {
|
|
385
|
+
yield 1;
|
|
386
|
+
yield 2;
|
|
387
|
+
yield 3;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
it('should apply the mapping function to each value', async () => {
|
|
391
|
+
const mapFunc = (value:any) => value * 2;
|
|
392
|
+
const generator = generatorMap(testGenerator(), mapFunc);
|
|
393
|
+
const result = [];
|
|
394
|
+
|
|
395
|
+
for await (const item of generator) {
|
|
396
|
+
result.push(item);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
expect(result).toEqual([2, 4, 6]);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('should pass the index and generator to the mapping function', async () => {
|
|
403
|
+
const mapFunc = (value:any, index:number) => `${value}-${index}`;
|
|
404
|
+
const generator = generatorMap(testGenerator(), mapFunc);
|
|
405
|
+
const result = [];
|
|
406
|
+
|
|
407
|
+
for await (const item of generator) {
|
|
408
|
+
result.push(item);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
expect(result).toEqual(['1-0', '2-1', '3-2']);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should handle an empty generator', async () => {
|
|
415
|
+
async function* emptyGenerator() {}
|
|
416
|
+
const generator = generatorMap(emptyGenerator(), (value) => value);
|
|
417
|
+
const result = [] as Array<any>;
|
|
418
|
+
|
|
419
|
+
for await (const item of generator) {
|
|
420
|
+
result.push(item);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
expect(result).toEqual([]);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
describe('batchGen additional tests', () => {
|
|
428
|
+
async function* testGenerator() {
|
|
429
|
+
yield 1;
|
|
430
|
+
yield 2;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
it('should yield single-item batches when batchSize is 1', async () => {
|
|
434
|
+
const generator = batchGen(testGenerator(), 1);
|
|
435
|
+
const result = [];
|
|
436
|
+
|
|
437
|
+
for await (const batch of generator) {
|
|
438
|
+
result.push(batch);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
expect(result).toEqual([[1], [2]]);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('should handle batchSize of 0 by throwing an error', async () => {
|
|
445
|
+
expect(async() => batchGen(testGenerator(), 0).next()).rejects.toThrow();
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('should propagate errors from the input generator', async () => {
|
|
449
|
+
async function* errorGenerator() {
|
|
450
|
+
yield 1;
|
|
451
|
+
throw new Error('Test error');
|
|
452
|
+
}
|
|
453
|
+
const generator = batchGen(errorGenerator(), 2);
|
|
454
|
+
const result = [];
|
|
455
|
+
|
|
456
|
+
await expect(async () => {
|
|
457
|
+
for await (const batch of generator) {
|
|
458
|
+
result.push(batch);
|
|
459
|
+
}
|
|
460
|
+
}).rejects.toThrow('Test error');
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
describe('renderAsyncGen additional tests', () => {
|
|
465
|
+
async function* testGenerator() {
|
|
466
|
+
yield null;
|
|
467
|
+
yield undefined;
|
|
468
|
+
yield 3;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
it('should handle null and undefined values', async () => {
|
|
472
|
+
const result = await renderAsyncGen(testGenerator());
|
|
473
|
+
|
|
474
|
+
expect(result).toEqual([null, undefined, 3]);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('should propagate errors from the input generator', async () => {
|
|
478
|
+
async function* errorGenerator() {
|
|
479
|
+
yield 1;
|
|
480
|
+
throw new Error('Test error');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
await expect(renderAsyncGen(errorGenerator())).rejects.toThrow('Test error');
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
describe('generatorMap additional tests', () => {
|
|
488
|
+
async function* testGenerator() {
|
|
489
|
+
yield 1;
|
|
490
|
+
yield 2;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
it('should handle nextMapFunc returning undefined or null', async () => {
|
|
494
|
+
const mapFunc = (value:any) => value === 1 ? undefined : null;
|
|
495
|
+
const generator = generatorMap(testGenerator(), mapFunc);
|
|
496
|
+
const result = [];
|
|
497
|
+
|
|
498
|
+
for await (const item of generator) {
|
|
499
|
+
result.push(item);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
expect(result).toEqual([undefined, null]);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it('should propagate errors from nextMapFunc', async () => {
|
|
506
|
+
const mapFunc = () => {
|
|
507
|
+
throw new Error('Map error');
|
|
508
|
+
};
|
|
509
|
+
const generator = generatorMap(testGenerator(), mapFunc);
|
|
510
|
+
|
|
511
|
+
await expect(async () => {
|
|
512
|
+
for await (const item of generator) {}
|
|
513
|
+
}).rejects.toThrow('Map error');
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('should handle asynchronous nextMapFunc', async () => {
|
|
517
|
+
const mapFunc = async (value:any) => value * 2;
|
|
518
|
+
const generator = generatorMap(testGenerator(), mapFunc);
|
|
519
|
+
const result = [];
|
|
520
|
+
|
|
521
|
+
for await (const item of generator) {
|
|
522
|
+
result.push(item);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
expect(result).toEqual([2, 4]);
|
|
526
|
+
});
|
|
527
|
+
});
|