@tellescope/sdk 1.236.1 → 1.236.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.
Files changed (32) hide show
  1. package/lib/cjs/enduser.d.ts +20 -0
  2. package/lib/cjs/enduser.d.ts.map +1 -1
  3. package/lib/cjs/sdk.d.ts +42 -0
  4. package/lib/cjs/sdk.d.ts.map +1 -1
  5. package/lib/cjs/sdk.js +1 -0
  6. package/lib/cjs/sdk.js.map +1 -1
  7. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.d.ts +9 -0
  8. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.d.ts.map +1 -0
  9. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js +1399 -0
  10. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js.map +1 -0
  11. package/lib/cjs/tests/tests.d.ts.map +1 -1
  12. package/lib/cjs/tests/tests.js +111 -106
  13. package/lib/cjs/tests/tests.js.map +1 -1
  14. package/lib/esm/enduser.d.ts +20 -0
  15. package/lib/esm/enduser.d.ts.map +1 -1
  16. package/lib/esm/sdk.d.ts +42 -0
  17. package/lib/esm/sdk.d.ts.map +1 -1
  18. package/lib/esm/sdk.js +1 -0
  19. package/lib/esm/sdk.js.map +1 -1
  20. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.d.ts +9 -0
  21. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.d.ts.map +1 -0
  22. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js +1372 -0
  23. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js.map +1 -0
  24. package/lib/esm/tests/tests.d.ts.map +1 -1
  25. package/lib/esm/tests/tests.js +111 -106
  26. package/lib/esm/tests/tests.js.map +1 -1
  27. package/lib/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +10 -10
  29. package/src/sdk.ts +2 -1
  30. package/src/tests/api_tests/auto_merge_form_submission.test.ts +876 -0
  31. package/src/tests/tests.ts +4 -1
  32. package/test_generated.pdf +0 -0
@@ -0,0 +1,1372 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ require('source-map-support').install();
38
+ import * as buffer from 'buffer';
39
+ import { Session, EnduserSession } from "../../sdk";
40
+ import { async_test, log_header, } from "@tellescope/testing";
41
+ import { setup_tests } from "../setup";
42
+ var host = process.env.API_URL || 'http://localhost:8080';
43
+ /**
44
+ * Helper: Create a form with auto-merge enabled and intake fields
45
+ */
46
+ var createAutoMergeForm = function (sdk, autoMergeOnSubmission) {
47
+ if (autoMergeOnSubmission === void 0) { autoMergeOnSubmission = true; }
48
+ return __awaiter(void 0, void 0, void 0, function () {
49
+ var form, fnameField, lnameField, emailField, phoneField, dobField, fields;
50
+ return __generator(this, function (_a) {
51
+ switch (_a.label) {
52
+ case 0: return [4 /*yield*/, sdk.api.forms.createOne({
53
+ title: 'Auto Merge Test Form',
54
+ allowPublicURL: true,
55
+ autoMergeOnSubmission: autoMergeOnSubmission,
56
+ })
57
+ // Add intake fields - must create sequentially due to previousFields dependencies
58
+ ];
59
+ case 1:
60
+ form = _a.sent();
61
+ return [4 /*yield*/, sdk.api.form_fields.createOne({
62
+ formId: form.id,
63
+ title: 'First Name',
64
+ type: 'string',
65
+ intakeField: 'fname',
66
+ previousFields: [{ type: 'root', info: {} }]
67
+ })];
68
+ case 2:
69
+ fnameField = _a.sent();
70
+ return [4 /*yield*/, sdk.api.form_fields.createOne({
71
+ formId: form.id,
72
+ title: 'Last Name',
73
+ type: 'string',
74
+ intakeField: 'lname',
75
+ previousFields: [{ type: 'after', info: { fieldId: fnameField.id } }]
76
+ })];
77
+ case 3:
78
+ lnameField = _a.sent();
79
+ return [4 /*yield*/, sdk.api.form_fields.createOne({
80
+ formId: form.id,
81
+ title: 'Email',
82
+ type: 'email',
83
+ intakeField: 'email',
84
+ previousFields: [{ type: 'after', info: { fieldId: lnameField.id } }]
85
+ })];
86
+ case 4:
87
+ emailField = _a.sent();
88
+ return [4 /*yield*/, sdk.api.form_fields.createOne({
89
+ formId: form.id,
90
+ title: 'Phone',
91
+ type: 'phone',
92
+ intakeField: 'phone',
93
+ previousFields: [{ type: 'after', info: { fieldId: emailField.id } }]
94
+ })];
95
+ case 5:
96
+ phoneField = _a.sent();
97
+ return [4 /*yield*/, sdk.api.form_fields.createOne({
98
+ formId: form.id,
99
+ title: 'Date of Birth',
100
+ type: 'dateString',
101
+ intakeField: 'dateOfBirth',
102
+ previousFields: [{ type: 'after', info: { fieldId: phoneField.id } }]
103
+ })];
104
+ case 6:
105
+ dobField = _a.sent();
106
+ fields = [fnameField, lnameField, emailField, phoneField, dobField];
107
+ return [2 /*return*/, { form: form, fields: fields }];
108
+ }
109
+ });
110
+ });
111
+ };
112
+ /**
113
+ * Helper: Submit a public form with skipMatch and get the created enduser ID
114
+ */
115
+ var submitPublicFormWithSkipMatch = function (form, fields, values) { return __awaiter(void 0, void 0, void 0, function () {
116
+ var enduserSDK, _a, authToken, accessCode, enduserId, authedSDK, responses, fnameField, lnameField, emailField, phoneField, dobField;
117
+ return __generator(this, function (_b) {
118
+ switch (_b.label) {
119
+ case 0:
120
+ enduserSDK = new EnduserSession({ host: host, businessId: form.businessId });
121
+ return [4 /*yield*/, enduserSDK.api.form_responses.session_for_public_form({
122
+ formId: form.id,
123
+ businessId: form.businessId,
124
+ skipMatch: true,
125
+ })];
126
+ case 1:
127
+ _a = _b.sent(), authToken = _a.authToken, accessCode = _a.accessCode, enduserId = _a.enduserId;
128
+ authedSDK = new EnduserSession({ host: host, businessId: form.businessId, authToken: authToken });
129
+ responses = [];
130
+ fnameField = fields.find(function (f) { return f.intakeField === 'fname'; });
131
+ lnameField = fields.find(function (f) { return f.intakeField === 'lname'; });
132
+ emailField = fields.find(function (f) { return f.intakeField === 'email'; });
133
+ phoneField = fields.find(function (f) { return f.intakeField === 'phone'; });
134
+ dobField = fields.find(function (f) { return f.intakeField === 'dateOfBirth'; });
135
+ if (values.fname && fnameField) {
136
+ responses.push({ fieldId: fnameField.id, fieldTitle: fnameField.title, answer: { type: 'string', value: values.fname } });
137
+ }
138
+ if (values.lname && lnameField) {
139
+ responses.push({ fieldId: lnameField.id, fieldTitle: lnameField.title, answer: { type: 'string', value: values.lname } });
140
+ }
141
+ if (values.email && emailField) {
142
+ responses.push({ fieldId: emailField.id, fieldTitle: emailField.title, answer: { type: 'email', value: values.email } });
143
+ }
144
+ if (values.phone && phoneField) {
145
+ responses.push({ fieldId: phoneField.id, fieldTitle: phoneField.title, answer: { type: 'phone', value: values.phone } });
146
+ }
147
+ if (values.dateOfBirth && dobField) {
148
+ responses.push({ fieldId: dobField.id, fieldTitle: dobField.title, answer: { type: 'dateString', value: values.dateOfBirth } });
149
+ }
150
+ return [4 /*yield*/, authedSDK.api.form_responses.submit_form_response({
151
+ accessCode: accessCode,
152
+ responses: responses,
153
+ })];
154
+ case 2:
155
+ _b.sent();
156
+ return [2 /*return*/, { enduserId: enduserId, accessCode: accessCode, authedSDK: authedSDK }];
157
+ }
158
+ });
159
+ }); };
160
+ /**
161
+ * Helper: Check if enduser has been deleted (immediate check, no polling)
162
+ * Since auto-merge is now synchronous, we don't need to poll
163
+ */
164
+ var isEnduserDeleted = function (sdk, enduserId) { return __awaiter(void 0, void 0, void 0, function () {
165
+ var _a;
166
+ return __generator(this, function (_b) {
167
+ switch (_b.label) {
168
+ case 0:
169
+ _b.trys.push([0, 2, , 3]);
170
+ return [4 /*yield*/, sdk.api.endusers.getOne(enduserId)];
171
+ case 1:
172
+ _b.sent();
173
+ return [2 /*return*/, false]; // Still exists
174
+ case 2:
175
+ _a = _b.sent();
176
+ return [2 /*return*/, true]; // Deleted
177
+ case 3: return [2 /*return*/];
178
+ }
179
+ });
180
+ }); };
181
+ /**
182
+ * Main test function that can be called independently or as part of the test suite
183
+ */
184
+ export var auto_merge_form_submission_tests = function (_a) {
185
+ var sdk = _a.sdk, sdkNonAdmin = _a.sdkNonAdmin;
186
+ return __awaiter(void 0, void 0, void 0, function () {
187
+ return __generator(this, function (_b) {
188
+ switch (_b.label) {
189
+ case 0:
190
+ log_header("Auto-Merge Form Submission Tests");
191
+ // Test 1: Happy Path - Merge by Email Match
192
+ return [4 /*yield*/, async_test("Auto-merge: Merge occurs when matching by email", function () { return __awaiter(void 0, void 0, void 0, function () {
193
+ var testEmail, destination, _a, form, fields, sourceId, sourceDeleted, updatedDestination, formResponses;
194
+ var _b;
195
+ return __generator(this, function (_c) {
196
+ switch (_c.label) {
197
+ case 0:
198
+ testEmail = "automerge.email.".concat(Date.now(), "@test.com");
199
+ return [4 /*yield*/, sdk.api.endusers.createOne({
200
+ fname: 'John',
201
+ lname: 'Doe',
202
+ email: testEmail
203
+ })];
204
+ case 1:
205
+ destination = _c.sent();
206
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)];
207
+ case 2:
208
+ _a = _c.sent(), form = _a.form, fields = _a.fields;
209
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
210
+ fname: 'John',
211
+ lname: 'Doe',
212
+ email: testEmail,
213
+ })
214
+ // Merge is synchronous - source should be deleted immediately after submission
215
+ ];
216
+ case 3:
217
+ sourceId = (_c.sent()).enduserId;
218
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
219
+ case 4:
220
+ sourceDeleted = _c.sent();
221
+ return [4 /*yield*/, sdk.api.endusers.getOne(destination.id)];
222
+ case 5:
223
+ updatedDestination = _c.sent();
224
+ return [4 /*yield*/, sdk.api.form_responses.getSome({ filter: { enduserId: destination.id } })
225
+ // Cleanup
226
+ ];
227
+ case 6:
228
+ formResponses = _c.sent();
229
+ // Cleanup
230
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
231
+ case 7:
232
+ // Cleanup
233
+ _c.sent();
234
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
235
+ case 8:
236
+ _c.sent();
237
+ return [2 /*return*/, sourceDeleted
238
+ && ((_b = updatedDestination.mergedIds) === null || _b === void 0 ? void 0 : _b.includes(sourceId))
239
+ && formResponses.length === 1];
240
+ }
241
+ });
242
+ }); }, { expectedResult: true })
243
+ // Test 2: Happy Path - Merge by Phone Match
244
+ ];
245
+ case 1:
246
+ // Test 1: Happy Path - Merge by Email Match
247
+ _b.sent();
248
+ // Test 2: Happy Path - Merge by Phone Match
249
+ return [4 /*yield*/, async_test("Auto-merge: Merge occurs when matching by phone", function () { return __awaiter(void 0, void 0, void 0, function () {
250
+ var testPhone, destination, _a, form, fields, sourceId, sourceDeleted, updatedDestination;
251
+ var _b;
252
+ return __generator(this, function (_c) {
253
+ switch (_c.label) {
254
+ case 0:
255
+ testPhone = '+15555551234';
256
+ return [4 /*yield*/, sdk.api.endusers.createOne({
257
+ fname: 'Jane',
258
+ lname: 'Smith',
259
+ phone: testPhone
260
+ })];
261
+ case 1:
262
+ destination = _c.sent();
263
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)];
264
+ case 2:
265
+ _a = _c.sent(), form = _a.form, fields = _a.fields;
266
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
267
+ fname: 'Jane',
268
+ lname: 'Smith',
269
+ phone: testPhone,
270
+ })
271
+ // Merge is synchronous - source should be deleted immediately after submission
272
+ ];
273
+ case 3:
274
+ sourceId = (_c.sent()).enduserId;
275
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
276
+ case 4:
277
+ sourceDeleted = _c.sent();
278
+ return [4 /*yield*/, sdk.api.endusers.getOne(destination.id)
279
+ // Cleanup
280
+ ];
281
+ case 5:
282
+ updatedDestination = _c.sent();
283
+ // Cleanup
284
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
285
+ case 6:
286
+ // Cleanup
287
+ _c.sent();
288
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
289
+ case 7:
290
+ _c.sent();
291
+ return [2 /*return*/, sourceDeleted && ((_b = updatedDestination.mergedIds) === null || _b === void 0 ? void 0 : _b.includes(sourceId))];
292
+ }
293
+ });
294
+ }); }, { expectedResult: true })
295
+ // Test 3: Happy Path - Merge by DateOfBirth Match
296
+ ];
297
+ case 2:
298
+ // Test 2: Happy Path - Merge by Phone Match
299
+ _b.sent();
300
+ // Test 3: Happy Path - Merge by DateOfBirth Match
301
+ return [4 /*yield*/, async_test("Auto-merge: Merge occurs when matching by dateOfBirth", function () { return __awaiter(void 0, void 0, void 0, function () {
302
+ var testDOB, destination, _a, form, fields, sourceId, sourceDeleted, updatedDestination;
303
+ var _b;
304
+ return __generator(this, function (_c) {
305
+ switch (_c.label) {
306
+ case 0:
307
+ testDOB = '1990-05-15';
308
+ return [4 /*yield*/, sdk.api.endusers.createOne({
309
+ fname: 'Bob',
310
+ lname: 'Johnson',
311
+ dateOfBirth: testDOB
312
+ })];
313
+ case 1:
314
+ destination = _c.sent();
315
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)];
316
+ case 2:
317
+ _a = _c.sent(), form = _a.form, fields = _a.fields;
318
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
319
+ fname: 'Bob',
320
+ lname: 'Johnson',
321
+ dateOfBirth: testDOB,
322
+ })
323
+ // Merge is synchronous - source should be deleted immediately after submission
324
+ ];
325
+ case 3:
326
+ sourceId = (_c.sent()).enduserId;
327
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
328
+ case 4:
329
+ sourceDeleted = _c.sent();
330
+ return [4 /*yield*/, sdk.api.endusers.getOne(destination.id)
331
+ // Cleanup
332
+ ];
333
+ case 5:
334
+ updatedDestination = _c.sent();
335
+ // Cleanup
336
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
337
+ case 6:
338
+ // Cleanup
339
+ _c.sent();
340
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
341
+ case 7:
342
+ _c.sent();
343
+ return [2 /*return*/, sourceDeleted && ((_b = updatedDestination.mergedIds) === null || _b === void 0 ? void 0 : _b.includes(sourceId))];
344
+ }
345
+ });
346
+ }); }, { expectedResult: true })
347
+ // Test 4: No Merge - Multiple Matches
348
+ ];
349
+ case 3:
350
+ // Test 3: Happy Path - Merge by DateOfBirth Match
351
+ _b.sent();
352
+ // Test 4: No Merge - Multiple Matches
353
+ return [4 /*yield*/, async_test("Auto-merge: No merge when multiple matches found", function () { return __awaiter(void 0, void 0, void 0, function () {
354
+ var testDOB, destination1, destination2, _a, form, fields, sourceId, sourceDeleted, dest1, dest2;
355
+ var _b, _c;
356
+ return __generator(this, function (_d) {
357
+ switch (_d.label) {
358
+ case 0:
359
+ testDOB = '1975-01-15';
360
+ return [4 /*yield*/, sdk.api.endusers.createOne({
361
+ fname: 'Multi',
362
+ lname: 'Match',
363
+ dateOfBirth: testDOB
364
+ })];
365
+ case 1:
366
+ destination1 = _d.sent();
367
+ return [4 /*yield*/, sdk.api.endusers.createOne({
368
+ fname: 'Multi',
369
+ lname: 'Match',
370
+ dateOfBirth: testDOB
371
+ })];
372
+ case 2:
373
+ destination2 = _d.sent();
374
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)];
375
+ case 3:
376
+ _a = _d.sent(), form = _a.form, fields = _a.fields;
377
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
378
+ fname: 'Multi',
379
+ lname: 'Match',
380
+ dateOfBirth: testDOB,
381
+ })
382
+ // Merge is synchronous - no need to wait, source should still exist
383
+ ];
384
+ case 4:
385
+ sourceId = (_d.sent()).enduserId;
386
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
387
+ case 5:
388
+ sourceDeleted = _d.sent();
389
+ return [4 /*yield*/, sdk.api.endusers.getOne(destination1.id)];
390
+ case 6:
391
+ dest1 = _d.sent();
392
+ return [4 /*yield*/, sdk.api.endusers.getOne(destination2.id)
393
+ // Cleanup
394
+ ];
395
+ case 7:
396
+ dest2 = _d.sent();
397
+ // Cleanup
398
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
399
+ case 8:
400
+ // Cleanup
401
+ _d.sent();
402
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination1.id)];
403
+ case 9:
404
+ _d.sent();
405
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination2.id)];
406
+ case 10:
407
+ _d.sent();
408
+ if (!!sourceDeleted) return [3 /*break*/, 12];
409
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(sourceId)];
410
+ case 11:
411
+ _d.sent();
412
+ _d.label = 12;
413
+ case 12: return [2 /*return*/, !sourceDeleted // Source should NOT be deleted
414
+ && !((_b = dest1.mergedIds) === null || _b === void 0 ? void 0 : _b.includes(sourceId))
415
+ && !((_c = dest2.mergedIds) === null || _c === void 0 ? void 0 : _c.includes(sourceId))];
416
+ }
417
+ });
418
+ }); }, { expectedResult: true })
419
+ // Test 5: No Merge - autoMergeOnSubmission Disabled
420
+ ];
421
+ case 4:
422
+ // Test 4: No Merge - Multiple Matches
423
+ _b.sent();
424
+ // Test 5: No Merge - autoMergeOnSubmission Disabled
425
+ return [4 /*yield*/, async_test("Auto-merge: No merge when autoMergeOnSubmission is disabled", function () { return __awaiter(void 0, void 0, void 0, function () {
426
+ var testEmail, destination, _a, form, fields, sourceId, sourceDeleted;
427
+ return __generator(this, function (_b) {
428
+ switch (_b.label) {
429
+ case 0:
430
+ testEmail = "automerge.disabled.".concat(Date.now(), "@test.com");
431
+ return [4 /*yield*/, sdk.api.endusers.createOne({
432
+ fname: 'Disabled',
433
+ lname: 'Test',
434
+ email: testEmail
435
+ })];
436
+ case 1:
437
+ destination = _b.sent();
438
+ return [4 /*yield*/, createAutoMergeForm(sdk, false)]; // Disabled
439
+ case 2:
440
+ _a = _b.sent() // Disabled
441
+ , form = _a.form, fields = _a.fields;
442
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
443
+ fname: 'Disabled',
444
+ lname: 'Test',
445
+ email: testEmail,
446
+ })
447
+ // Merge is synchronous - no need to wait, source should still exist
448
+ ];
449
+ case 3:
450
+ sourceId = (_b.sent()).enduserId;
451
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)
452
+ // Cleanup
453
+ ];
454
+ case 4:
455
+ sourceDeleted = _b.sent();
456
+ // Cleanup
457
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
458
+ case 5:
459
+ // Cleanup
460
+ _b.sent();
461
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
462
+ case 6:
463
+ _b.sent();
464
+ if (!!sourceDeleted) return [3 /*break*/, 8];
465
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(sourceId)];
466
+ case 7:
467
+ _b.sent();
468
+ _b.label = 8;
469
+ case 8: return [2 /*return*/, !sourceDeleted]; // Source should NOT be deleted
470
+ }
471
+ });
472
+ }); }, { expectedResult: true })
473
+ // Test 6: No Merge - No Matching Enduser
474
+ ];
475
+ case 5:
476
+ // Test 5: No Merge - autoMergeOnSubmission Disabled
477
+ _b.sent();
478
+ // Test 6: No Merge - No Matching Enduser
479
+ return [4 /*yield*/, async_test("Auto-merge: No merge when no matching enduser exists", function () { return __awaiter(void 0, void 0, void 0, function () {
480
+ var _a, form, fields, sourceId, sourceDeleted;
481
+ return __generator(this, function (_b) {
482
+ switch (_b.label) {
483
+ case 0: return [4 /*yield*/, createAutoMergeForm(sdk, true)];
484
+ case 1:
485
+ _a = _b.sent(), form = _a.form, fields = _a.fields;
486
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
487
+ fname: 'NoMatch',
488
+ lname: 'Person',
489
+ email: "nomatch.".concat(Date.now(), "@test.com"),
490
+ })
491
+ // Merge is synchronous - no need to wait, source should still exist (no match found)
492
+ ];
493
+ case 2:
494
+ sourceId = (_b.sent()).enduserId;
495
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)
496
+ // Cleanup
497
+ ];
498
+ case 3:
499
+ sourceDeleted = _b.sent();
500
+ // Cleanup
501
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
502
+ case 4:
503
+ // Cleanup
504
+ _b.sent();
505
+ if (!!sourceDeleted) return [3 /*break*/, 6];
506
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(sourceId)];
507
+ case 5:
508
+ _b.sent();
509
+ _b.label = 6;
510
+ case 6: return [2 /*return*/, !sourceDeleted]; // Source should NOT be deleted
511
+ }
512
+ });
513
+ }); }, { expectedResult: true })
514
+ // Test 7: Case Sensitive Matching - No Merge When Case Differs
515
+ ];
516
+ case 6:
517
+ // Test 6: No Merge - No Matching Enduser
518
+ _b.sent();
519
+ // Test 7: Case Sensitive Matching - No Merge When Case Differs
520
+ return [4 /*yield*/, async_test("Auto-merge: No merge when case differs (case-sensitive matching)", function () { return __awaiter(void 0, void 0, void 0, function () {
521
+ var testEmail, destination, _a, form, fields, sourceId, sourceDeleted, updatedDestination;
522
+ var _b;
523
+ return __generator(this, function (_c) {
524
+ switch (_c.label) {
525
+ case 0:
526
+ testEmail = "automerge.case.".concat(Date.now(), "@test.com");
527
+ return [4 /*yield*/, sdk.api.endusers.createOne({
528
+ fname: 'Case',
529
+ lname: 'Test',
530
+ email: testEmail
531
+ })];
532
+ case 1:
533
+ destination = _c.sent();
534
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)
535
+ // Submit with different case - should NOT match due to case-sensitive matching
536
+ ];
537
+ case 2:
538
+ _a = _c.sent(), form = _a.form, fields = _a.fields;
539
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
540
+ fname: 'CASE',
541
+ lname: 'TEST',
542
+ email: testEmail.toUpperCase(), // Different case
543
+ })
544
+ // Merge is case-sensitive - source should still exist (no match found)
545
+ ];
546
+ case 3:
547
+ sourceId = (_c.sent()).enduserId;
548
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
549
+ case 4:
550
+ sourceDeleted = _c.sent();
551
+ return [4 /*yield*/, sdk.api.endusers.getOne(destination.id)
552
+ // Cleanup
553
+ ];
554
+ case 5:
555
+ updatedDestination = _c.sent();
556
+ // Cleanup
557
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
558
+ case 6:
559
+ // Cleanup
560
+ _c.sent();
561
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
562
+ case 7:
563
+ _c.sent();
564
+ if (!!sourceDeleted) return [3 /*break*/, 9];
565
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(sourceId)];
566
+ case 8:
567
+ _c.sent();
568
+ _c.label = 9;
569
+ case 9: return [2 /*return*/, !sourceDeleted // Source should NOT be deleted (no merge)
570
+ && !((_b = updatedDestination.mergedIds) === null || _b === void 0 ? void 0 : _b.includes(sourceId))];
571
+ }
572
+ });
573
+ }); }, { expectedResult: true })
574
+ // Test 8: eligibleForAutoMerge Flag Verification
575
+ ];
576
+ case 7:
577
+ // Test 7: Case Sensitive Matching - No Merge When Case Differs
578
+ _b.sent();
579
+ // Test 8: eligibleForAutoMerge Flag Verification
580
+ return [4 /*yield*/, async_test("Auto-merge: eligibleForAutoMerge flag is set correctly", function () { return __awaiter(void 0, void 0, void 0, function () {
581
+ var _a, formEnabled, fieldsEnabled, enduserSDKEnabled, enabledEnduserId, enabledEnduser, _b, formDisabled, fieldsDisabled, enduserSDKDisabled, disabledEnduserId, disabledEnduser;
582
+ return __generator(this, function (_c) {
583
+ switch (_c.label) {
584
+ case 0: return [4 /*yield*/, createAutoMergeForm(sdk, true)];
585
+ case 1:
586
+ _a = _c.sent(), formEnabled = _a.form, fieldsEnabled = _a.fields;
587
+ enduserSDKEnabled = new EnduserSession({ host: host, businessId: formEnabled.businessId });
588
+ return [4 /*yield*/, enduserSDKEnabled.api.form_responses.session_for_public_form({
589
+ formId: formEnabled.id,
590
+ businessId: formEnabled.businessId,
591
+ skipMatch: true,
592
+ })];
593
+ case 2:
594
+ enabledEnduserId = (_c.sent()).enduserId;
595
+ return [4 /*yield*/, sdk.api.endusers.getOne(enabledEnduserId)
596
+ // Form with autoMergeOnSubmission: false
597
+ ];
598
+ case 3:
599
+ enabledEnduser = _c.sent();
600
+ return [4 /*yield*/, createAutoMergeForm(sdk, false)];
601
+ case 4:
602
+ _b = _c.sent(), formDisabled = _b.form, fieldsDisabled = _b.fields;
603
+ enduserSDKDisabled = new EnduserSession({ host: host, businessId: formDisabled.businessId });
604
+ return [4 /*yield*/, enduserSDKDisabled.api.form_responses.session_for_public_form({
605
+ formId: formDisabled.id,
606
+ businessId: formDisabled.businessId,
607
+ skipMatch: true,
608
+ })];
609
+ case 5:
610
+ disabledEnduserId = (_c.sent()).enduserId;
611
+ return [4 /*yield*/, sdk.api.endusers.getOne(disabledEnduserId)
612
+ // Cleanup
613
+ ];
614
+ case 6:
615
+ disabledEnduser = _c.sent();
616
+ // Cleanup
617
+ return [4 /*yield*/, sdk.api.forms.deleteOne(formEnabled.id)];
618
+ case 7:
619
+ // Cleanup
620
+ _c.sent();
621
+ return [4 /*yield*/, sdk.api.forms.deleteOne(formDisabled.id)];
622
+ case 8:
623
+ _c.sent();
624
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(enabledEnduserId)];
625
+ case 9:
626
+ _c.sent();
627
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(disabledEnduserId)];
628
+ case 10:
629
+ _c.sent();
630
+ return [2 /*return*/, enabledEnduser.eligibleForAutoMerge === true
631
+ && disabledEnduser.eligibleForAutoMerge !== true];
632
+ }
633
+ });
634
+ }); }, { expectedResult: true })
635
+ // Test 9: Files Transfer on Merge
636
+ ];
637
+ case 8:
638
+ // Test 8: eligibleForAutoMerge Flag Verification
639
+ _b.sent();
640
+ // Test 9: Files Transfer on Merge
641
+ return [4 /*yield*/, async_test("Auto-merge: Files are transferred to destination enduser", function () { return __awaiter(void 0, void 0, void 0, function () {
642
+ var testEmail, destination, _a, form, fields, enduserSDK, _b, authToken, accessCode, sourceId, buff, _c, presignedUpload, file, authedSDK, fnameField, lnameField, emailField, sourceDeleted, updatedFile;
643
+ return __generator(this, function (_d) {
644
+ switch (_d.label) {
645
+ case 0:
646
+ testEmail = "automerge.files.".concat(Date.now(), "@test.com");
647
+ return [4 /*yield*/, sdk.api.endusers.createOne({
648
+ fname: 'Files',
649
+ lname: 'Test',
650
+ email: testEmail
651
+ })];
652
+ case 1:
653
+ destination = _d.sent();
654
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)
655
+ // Create public session to get source enduser
656
+ ];
657
+ case 2:
658
+ _a = _d.sent(), form = _a.form, fields = _a.fields;
659
+ enduserSDK = new EnduserSession({ host: host, businessId: form.businessId });
660
+ return [4 /*yield*/, enduserSDK.api.form_responses.session_for_public_form({
661
+ formId: form.id,
662
+ businessId: form.businessId,
663
+ skipMatch: true,
664
+ })
665
+ // Create a file for the source enduser using prepare_file_upload + UPLOAD
666
+ ];
667
+ case 3:
668
+ _b = _d.sent(), authToken = _b.authToken, accessCode = _b.accessCode, sourceId = _b.enduserId;
669
+ buff = buffer.Buffer.from('test file data for auto-merge');
670
+ return [4 /*yield*/, sdk.api.files.prepare_file_upload({
671
+ name: 'test-file.txt',
672
+ type: 'text/plain',
673
+ size: buff.byteLength,
674
+ enduserId: sourceId,
675
+ })];
676
+ case 4:
677
+ _c = _d.sent(), presignedUpload = _c.presignedUpload, file = _c.file;
678
+ return [4 /*yield*/, sdk.UPLOAD(presignedUpload, buff)
679
+ // Now submit the form to trigger merge
680
+ ];
681
+ case 5:
682
+ _d.sent();
683
+ authedSDK = new EnduserSession({ host: host, businessId: form.businessId, authToken: authToken });
684
+ fnameField = fields.find(function (f) { return f.intakeField === 'fname'; });
685
+ lnameField = fields.find(function (f) { return f.intakeField === 'lname'; });
686
+ emailField = fields.find(function (f) { return f.intakeField === 'email'; });
687
+ return [4 /*yield*/, authedSDK.api.form_responses.submit_form_response({
688
+ accessCode: accessCode,
689
+ responses: [
690
+ { fieldId: fnameField.id, fieldTitle: fnameField.title, answer: { type: 'string', value: 'Files' } },
691
+ { fieldId: lnameField.id, fieldTitle: lnameField.title, answer: { type: 'string', value: 'Test' } },
692
+ { fieldId: emailField.id, fieldTitle: emailField.title, answer: { type: 'email', value: testEmail } },
693
+ ],
694
+ })
695
+ // Merge is synchronous - source should be deleted immediately after submission
696
+ ];
697
+ case 6:
698
+ _d.sent();
699
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
700
+ case 7:
701
+ sourceDeleted = _d.sent();
702
+ return [4 /*yield*/, sdk.api.files.getOne(file.id)
703
+ // Cleanup
704
+ ];
705
+ case 8:
706
+ updatedFile = _d.sent();
707
+ // Cleanup
708
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
709
+ case 9:
710
+ // Cleanup
711
+ _d.sent();
712
+ return [4 /*yield*/, sdk.api.files.deleteOne(file.id)];
713
+ case 10:
714
+ _d.sent();
715
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
716
+ case 11:
717
+ _d.sent();
718
+ return [2 /*return*/, sourceDeleted && updatedFile.enduserId === destination.id];
719
+ }
720
+ });
721
+ }); }, { expectedResult: true })
722
+ // Test 10: Calendar Events Transfer on Merge
723
+ ];
724
+ case 9:
725
+ // Test 9: Files Transfer on Merge
726
+ _b.sent();
727
+ // Test 10: Calendar Events Transfer on Merge
728
+ return [4 /*yield*/, async_test("Auto-merge: Calendar events are transferred to destination enduser", function () { return __awaiter(void 0, void 0, void 0, function () {
729
+ var testEmail, destination, _a, form, fields, enduserSDK, _b, authToken, accessCode, sourceId, event, authedSDK, fnameField, lnameField, emailField, sourceDeleted, updatedEvent;
730
+ var _c;
731
+ return __generator(this, function (_d) {
732
+ switch (_d.label) {
733
+ case 0:
734
+ testEmail = "automerge.events.".concat(Date.now(), "@test.com");
735
+ return [4 /*yield*/, sdk.api.endusers.createOne({
736
+ fname: 'Events',
737
+ lname: 'Test',
738
+ email: testEmail
739
+ })];
740
+ case 1:
741
+ destination = _d.sent();
742
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)
743
+ // Create public session to get source enduser
744
+ ];
745
+ case 2:
746
+ _a = _d.sent(), form = _a.form, fields = _a.fields;
747
+ enduserSDK = new EnduserSession({ host: host, businessId: form.businessId });
748
+ return [4 /*yield*/, enduserSDK.api.form_responses.session_for_public_form({
749
+ formId: form.id,
750
+ businessId: form.businessId,
751
+ skipMatch: true,
752
+ })
753
+ // Create a calendar event with source enduser as attendee
754
+ ];
755
+ case 3:
756
+ _b = _d.sent(), authToken = _b.authToken, accessCode = _b.accessCode, sourceId = _b.enduserId;
757
+ return [4 /*yield*/, sdk.api.calendar_events.createOne({
758
+ title: 'Test Event',
759
+ startTimeInMS: Date.now() + 86400000,
760
+ durationInMinutes: 30,
761
+ attendees: [{ id: sourceId, type: 'enduser' }],
762
+ })
763
+ // Now submit the form to trigger merge
764
+ ];
765
+ case 4:
766
+ event = _d.sent();
767
+ authedSDK = new EnduserSession({ host: host, businessId: form.businessId, authToken: authToken });
768
+ fnameField = fields.find(function (f) { return f.intakeField === 'fname'; });
769
+ lnameField = fields.find(function (f) { return f.intakeField === 'lname'; });
770
+ emailField = fields.find(function (f) { return f.intakeField === 'email'; });
771
+ return [4 /*yield*/, authedSDK.api.form_responses.submit_form_response({
772
+ accessCode: accessCode,
773
+ responses: [
774
+ { fieldId: fnameField.id, fieldTitle: fnameField.title, answer: { type: 'string', value: 'Events' } },
775
+ { fieldId: lnameField.id, fieldTitle: lnameField.title, answer: { type: 'string', value: 'Test' } },
776
+ { fieldId: emailField.id, fieldTitle: emailField.title, answer: { type: 'email', value: testEmail } },
777
+ ],
778
+ })
779
+ // Merge is synchronous - source should be deleted immediately after submission
780
+ ];
781
+ case 5:
782
+ _d.sent();
783
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
784
+ case 6:
785
+ sourceDeleted = _d.sent();
786
+ return [4 /*yield*/, sdk.api.calendar_events.getOne(event.id)
787
+ // Cleanup
788
+ ];
789
+ case 7:
790
+ updatedEvent = _d.sent();
791
+ // Cleanup
792
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
793
+ case 8:
794
+ // Cleanup
795
+ _d.sent();
796
+ return [4 /*yield*/, sdk.api.calendar_events.deleteOne(event.id)];
797
+ case 9:
798
+ _d.sent();
799
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
800
+ case 10:
801
+ _d.sent();
802
+ return [2 /*return*/, sourceDeleted && ((_c = updatedEvent.attendees) === null || _c === void 0 ? void 0 : _c.some(function (a) { return a.id === destination.id; }))];
803
+ }
804
+ });
805
+ }); }, { expectedResult: true })
806
+ // Test 11: Form response enduserId is updated to destination (placeholder is updated before submission completes)
807
+ ];
808
+ case 10:
809
+ // Test 10: Calendar Events Transfer on Merge
810
+ _b.sent();
811
+ // Test 11: Form response enduserId is updated to destination (placeholder is updated before submission completes)
812
+ return [4 /*yield*/, async_test("Auto-merge: Form response enduserId is updated to destination", function () { return __awaiter(void 0, void 0, void 0, function () {
813
+ var testEmail, destination, _a, form, fields, _b, sourceId, accessCode, sourceDeleted, formResponses, createdFormResponse;
814
+ return __generator(this, function (_c) {
815
+ switch (_c.label) {
816
+ case 0:
817
+ testEmail = "automerge.directfr.".concat(Date.now(), "@test.com");
818
+ return [4 /*yield*/, sdk.api.endusers.createOne({
819
+ fname: 'Direct',
820
+ lname: 'Response',
821
+ email: testEmail
822
+ })];
823
+ case 1:
824
+ destination = _c.sent();
825
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)];
826
+ case 2:
827
+ _a = _c.sent(), form = _a.form, fields = _a.fields;
828
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
829
+ fname: 'Direct',
830
+ lname: 'Response',
831
+ email: testEmail,
832
+ })
833
+ // Merge is synchronous - verify form response was created with destination ID
834
+ ];
835
+ case 3:
836
+ _b = _c.sent(), sourceId = _b.enduserId, accessCode = _b.accessCode;
837
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)
838
+ // Get form responses by accessCode to find the one we created
839
+ ];
840
+ case 4:
841
+ sourceDeleted = _c.sent();
842
+ return [4 /*yield*/, sdk.api.form_responses.getSome({ filter: { accessCode: accessCode } })];
843
+ case 5:
844
+ formResponses = _c.sent();
845
+ createdFormResponse = formResponses[0];
846
+ // Cleanup
847
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
848
+ case 6:
849
+ // Cleanup
850
+ _c.sent();
851
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)
852
+ // The form response should have been created directly with destination enduser ID
853
+ // (not transferred after creation)
854
+ ];
855
+ case 7:
856
+ _c.sent();
857
+ // The form response should have been created directly with destination enduser ID
858
+ // (not transferred after creation)
859
+ return [2 /*return*/, sourceDeleted
860
+ && createdFormResponse !== undefined
861
+ && createdFormResponse.enduserId === destination.id];
862
+ }
863
+ });
864
+ }); }, { expectedResult: true })
865
+ // Test 12: Intake fields update destination enduser directly
866
+ ];
867
+ case 11:
868
+ // Test 11: Form response enduserId is updated to destination (placeholder is updated before submission completes)
869
+ _b.sent();
870
+ // Test 12: Intake fields update destination enduser directly
871
+ return [4 /*yield*/, async_test("Auto-merge: Intake fields update destination enduser directly", function () { return __awaiter(void 0, void 0, void 0, function () {
872
+ var testEmail, destination, _a, form, fields, newPhone, newDOB, sourceId, sourceDeleted, updatedDestination;
873
+ return __generator(this, function (_b) {
874
+ switch (_b.label) {
875
+ case 0:
876
+ testEmail = "automerge.intake.".concat(Date.now(), "@test.com");
877
+ return [4 /*yield*/, sdk.api.endusers.createOne({
878
+ fname: 'Intake',
879
+ lname: 'Test',
880
+ email: testEmail,
881
+ // No phone or DOB set initially
882
+ })];
883
+ case 1:
884
+ destination = _b.sent();
885
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)];
886
+ case 2:
887
+ _a = _b.sent(), form = _a.form, fields = _a.fields;
888
+ newPhone = '+15555559876';
889
+ newDOB = '1985-03-20';
890
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
891
+ fname: 'Intake',
892
+ lname: 'Test',
893
+ email: testEmail,
894
+ phone: newPhone,
895
+ dateOfBirth: newDOB,
896
+ })
897
+ // Merge is synchronous - verify intake fields updated destination directly
898
+ ];
899
+ case 3:
900
+ sourceId = (_b.sent()).enduserId;
901
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
902
+ case 4:
903
+ sourceDeleted = _b.sent();
904
+ return [4 /*yield*/, sdk.api.endusers.getOne(destination.id)
905
+ // Cleanup
906
+ ];
907
+ case 5:
908
+ updatedDestination = _b.sent();
909
+ // Cleanup
910
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
911
+ case 6:
912
+ // Cleanup
913
+ _b.sent();
914
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)
915
+ // Intake fields should have been applied to the destination enduser
916
+ ];
917
+ case 7:
918
+ _b.sent();
919
+ // Intake fields should have been applied to the destination enduser
920
+ return [2 /*return*/, sourceDeleted
921
+ && updatedDestination.phone === newPhone
922
+ && updatedDestination.dateOfBirth === newDOB];
923
+ }
924
+ });
925
+ }); }, { expectedResult: true })
926
+ // Test 13: eligibleForAutoMerge is unset after submission (no merge case)
927
+ ];
928
+ case 12:
929
+ // Test 12: Intake fields update destination enduser directly
930
+ _b.sent();
931
+ // Test 13: eligibleForAutoMerge is unset after submission (no merge case)
932
+ return [4 /*yield*/, async_test("Auto-merge: eligibleForAutoMerge is unset after submission when no merge occurs", function () { return __awaiter(void 0, void 0, void 0, function () {
933
+ var _a, form, fields, sourceId, updatedEnduser;
934
+ return __generator(this, function (_b) {
935
+ switch (_b.label) {
936
+ case 0: return [4 /*yield*/, createAutoMergeForm(sdk, true)
937
+ // Submit form - no match exists, so no merge will happen
938
+ ];
939
+ case 1:
940
+ _a = _b.sent(), form = _a.form, fields = _a.fields;
941
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
942
+ fname: 'Unset',
943
+ lname: 'Flag',
944
+ email: "unset.flag.".concat(Date.now(), "@test.com"),
945
+ })
946
+ // Verify enduser still exists and eligibleForAutoMerge is unset
947
+ ];
948
+ case 2:
949
+ sourceId = (_b.sent()).enduserId;
950
+ return [4 /*yield*/, sdk.api.endusers.getOne(sourceId)
951
+ // Cleanup
952
+ ];
953
+ case 3:
954
+ updatedEnduser = _b.sent();
955
+ // Cleanup
956
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
957
+ case 4:
958
+ // Cleanup
959
+ _b.sent();
960
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(sourceId)
961
+ // eligibleForAutoMerge should be unset (undefined/falsy) after submission
962
+ ];
963
+ case 5:
964
+ _b.sent();
965
+ // eligibleForAutoMerge should be unset (undefined/falsy) after submission
966
+ return [2 /*return*/, updatedEnduser.eligibleForAutoMerge !== true];
967
+ }
968
+ });
969
+ }); }, { expectedResult: true })
970
+ // ============================================
971
+ // BACKWARDS COMPATIBILITY & EDGE CASE TESTS
972
+ // ============================================
973
+ // Test 14: No merge when source enduser has multiple form responses
974
+ ];
975
+ case 13:
976
+ // Test 13: eligibleForAutoMerge is unset after submission (no merge case)
977
+ _b.sent();
978
+ // ============================================
979
+ // BACKWARDS COMPATIBILITY & EDGE CASE TESTS
980
+ // ============================================
981
+ // Test 14: No merge when source enduser has multiple form responses
982
+ return [4 /*yield*/, async_test("Auto-merge: No merge when source enduser already has multiple form responses", function () { return __awaiter(void 0, void 0, void 0, function () {
983
+ var testEmail, destination, _a, form1, fields1, _b, form2, fields2, enduserSDK1, session1, sourceId, authedSDK1, fnameField1, lnameField1, emailField1, enduserSDK2, session2, sourceId2, authedSDK2, fnameField2, lnameField2, emailField2, sourceStillExists;
984
+ return __generator(this, function (_c) {
985
+ switch (_c.label) {
986
+ case 0:
987
+ testEmail = "automerge.multiresponse.".concat(Date.now(), "@test.com");
988
+ return [4 /*yield*/, sdk.api.endusers.createOne({
989
+ fname: 'Multi',
990
+ lname: 'Response',
991
+ email: testEmail
992
+ })];
993
+ case 1:
994
+ destination = _c.sent();
995
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)];
996
+ case 2:
997
+ _a = _c.sent(), form1 = _a.form, fields1 = _a.fields;
998
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)
999
+ // Create first public session and submit (this creates first form response)
1000
+ ];
1001
+ case 3:
1002
+ _b = _c.sent(), form2 = _b.form, fields2 = _b.fields;
1003
+ enduserSDK1 = new EnduserSession({ host: host, businessId: form1.businessId });
1004
+ return [4 /*yield*/, enduserSDK1.api.form_responses.session_for_public_form({
1005
+ formId: form1.id,
1006
+ businessId: form1.businessId,
1007
+ skipMatch: true,
1008
+ })];
1009
+ case 4:
1010
+ session1 = _c.sent();
1011
+ sourceId = session1.enduserId;
1012
+ authedSDK1 = new EnduserSession({ host: host, businessId: form1.businessId, authToken: session1.authToken });
1013
+ fnameField1 = fields1.find(function (f) { return f.intakeField === 'fname'; });
1014
+ lnameField1 = fields1.find(function (f) { return f.intakeField === 'lname'; });
1015
+ emailField1 = fields1.find(function (f) { return f.intakeField === 'email'; });
1016
+ return [4 /*yield*/, authedSDK1.api.form_responses.submit_form_response({
1017
+ accessCode: session1.accessCode,
1018
+ responses: [
1019
+ { fieldId: fnameField1.id, fieldTitle: fnameField1.title, answer: { type: 'string', value: 'Different' } },
1020
+ { fieldId: lnameField1.id, fieldTitle: lnameField1.title, answer: { type: 'string', value: 'Person' } },
1021
+ { fieldId: emailField1.id, fieldTitle: emailField1.title, answer: { type: 'email', value: "different.".concat(Date.now(), "@test.com") } },
1022
+ ],
1023
+ })
1024
+ // Now create a second form response for the SAME source enduser via admin SDK
1025
+ ];
1026
+ case 5:
1027
+ _c.sent();
1028
+ // Now create a second form response for the SAME source enduser via admin SDK
1029
+ return [4 /*yield*/, sdk.api.form_responses.createOne({
1030
+ formId: form2.id,
1031
+ formTitle: 'Auto Merge Test Form',
1032
+ enduserId: sourceId,
1033
+ })
1034
+ // Re-set eligibleForAutoMerge manually to simulate another attempt
1035
+ ];
1036
+ case 6:
1037
+ // Now create a second form response for the SAME source enduser via admin SDK
1038
+ _c.sent();
1039
+ // Re-set eligibleForAutoMerge manually to simulate another attempt
1040
+ return [4 /*yield*/, sdk.api.endusers.updateOne(sourceId, { eligibleForAutoMerge: true })
1041
+ // Create another public session that would match - but source now has 2+ form responses
1042
+ ];
1043
+ case 7:
1044
+ // Re-set eligibleForAutoMerge manually to simulate another attempt
1045
+ _c.sent();
1046
+ enduserSDK2 = new EnduserSession({ host: host, businessId: form2.businessId });
1047
+ return [4 /*yield*/, enduserSDK2.api.form_responses.session_for_public_form({
1048
+ formId: form2.id,
1049
+ businessId: form2.businessId,
1050
+ skipMatch: true,
1051
+ })];
1052
+ case 8:
1053
+ session2 = _c.sent();
1054
+ sourceId2 = session2.enduserId;
1055
+ // Manually add another form response to sourceId2 to trigger the >1 check
1056
+ return [4 /*yield*/, sdk.api.form_responses.createOne({
1057
+ formId: form1.id,
1058
+ formTitle: 'Auto Merge Test Form',
1059
+ enduserId: sourceId2,
1060
+ })
1061
+ // Submit with matching data - should NOT merge because source has >1 form responses
1062
+ ];
1063
+ case 9:
1064
+ // Manually add another form response to sourceId2 to trigger the >1 check
1065
+ _c.sent();
1066
+ authedSDK2 = new EnduserSession({ host: host, businessId: form2.businessId, authToken: session2.authToken });
1067
+ fnameField2 = fields2.find(function (f) { return f.intakeField === 'fname'; });
1068
+ lnameField2 = fields2.find(function (f) { return f.intakeField === 'lname'; });
1069
+ emailField2 = fields2.find(function (f) { return f.intakeField === 'email'; });
1070
+ return [4 /*yield*/, authedSDK2.api.form_responses.submit_form_response({
1071
+ accessCode: session2.accessCode,
1072
+ responses: [
1073
+ { fieldId: fnameField2.id, fieldTitle: fnameField2.title, answer: { type: 'string', value: 'Multi' } },
1074
+ { fieldId: lnameField2.id, fieldTitle: lnameField2.title, answer: { type: 'string', value: 'Response' } },
1075
+ { fieldId: emailField2.id, fieldTitle: emailField2.title, answer: { type: 'email', value: testEmail } },
1076
+ ],
1077
+ })
1078
+ // Source should NOT be deleted because it had multiple form responses
1079
+ ];
1080
+ case 10:
1081
+ _c.sent();
1082
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId2)];
1083
+ case 11:
1084
+ sourceStillExists = !(_c.sent());
1085
+ // Cleanup
1086
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form1.id)];
1087
+ case 12:
1088
+ // Cleanup
1089
+ _c.sent();
1090
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form2.id)];
1091
+ case 13:
1092
+ _c.sent();
1093
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
1094
+ case 14:
1095
+ _c.sent();
1096
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(sourceId)];
1097
+ case 15:
1098
+ _c.sent();
1099
+ if (!sourceStillExists) return [3 /*break*/, 17];
1100
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(sourceId2)];
1101
+ case 16:
1102
+ _c.sent();
1103
+ _c.label = 17;
1104
+ case 17: return [2 /*return*/, sourceStillExists];
1105
+ }
1106
+ });
1107
+ }); }, { expectedResult: true })
1108
+ // Test 15: Backwards compat - skipMatch=false does NOT set eligibleForAutoMerge
1109
+ ];
1110
+ case 14:
1111
+ // ============================================
1112
+ // BACKWARDS COMPATIBILITY & EDGE CASE TESTS
1113
+ // ============================================
1114
+ // Test 14: No merge when source enduser has multiple form responses
1115
+ _b.sent();
1116
+ // Test 15: Backwards compat - skipMatch=false does NOT set eligibleForAutoMerge
1117
+ return [4 /*yield*/, async_test("Backwards compat: skipMatch=false does not set eligibleForAutoMerge even with autoMergeOnSubmission=true", function () { return __awaiter(void 0, void 0, void 0, function () {
1118
+ var _a, form, fields, testPhone, enduserSDK, enduserId, enduser;
1119
+ return __generator(this, function (_b) {
1120
+ switch (_b.label) {
1121
+ case 0: return [4 /*yield*/, createAutoMergeForm(sdk, true)];
1122
+ case 1:
1123
+ _a = _b.sent(), form = _a.form, fields = _a.fields;
1124
+ testPhone = "+1555555".concat(Date.now().toString().slice(-4));
1125
+ enduserSDK = new EnduserSession({ host: host, businessId: form.businessId });
1126
+ return [4 /*yield*/, enduserSDK.api.form_responses.session_for_public_form({
1127
+ formId: form.id,
1128
+ businessId: form.businessId,
1129
+ phone: testPhone, // Phone is required when skipMatch is not set
1130
+ // skipMatch is NOT set (defaults to false)
1131
+ })];
1132
+ case 2:
1133
+ enduserId = (_b.sent()).enduserId;
1134
+ return [4 /*yield*/, sdk.api.endusers.getOne(enduserId)
1135
+ // Cleanup
1136
+ ];
1137
+ case 3:
1138
+ enduser = _b.sent();
1139
+ // Cleanup
1140
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
1141
+ case 4:
1142
+ // Cleanup
1143
+ _b.sent();
1144
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(enduserId)
1145
+ // eligibleForAutoMerge should NOT be set when skipMatch is false
1146
+ ];
1147
+ case 5:
1148
+ _b.sent();
1149
+ // eligibleForAutoMerge should NOT be set when skipMatch is false
1150
+ return [2 /*return*/, enduser.eligibleForAutoMerge !== true];
1151
+ }
1152
+ });
1153
+ }); }, { expectedResult: true })
1154
+ // Test 16: Backwards compat - Private form submission doesn't trigger auto-merge
1155
+ ];
1156
+ case 15:
1157
+ // Test 15: Backwards compat - skipMatch=false does NOT set eligibleForAutoMerge
1158
+ _b.sent();
1159
+ // Test 16: Backwards compat - Private form submission doesn't trigger auto-merge
1160
+ return [4 /*yield*/, async_test("Backwards compat: Private form submission does not trigger auto-merge", function () { return __awaiter(void 0, void 0, void 0, function () {
1161
+ var testEmail, destination, source, _a, form, fields, fnameField, lnameField, emailField, sourceStillExists;
1162
+ return __generator(this, function (_b) {
1163
+ switch (_b.label) {
1164
+ case 0:
1165
+ testEmail = "backcompat.private.".concat(Date.now(), "@test.com");
1166
+ return [4 /*yield*/, sdk.api.endusers.createOne({
1167
+ fname: 'Private',
1168
+ lname: 'Test',
1169
+ email: testEmail
1170
+ })
1171
+ // Create source enduser with eligibleForAutoMerge manually set
1172
+ ];
1173
+ case 1:
1174
+ destination = _b.sent();
1175
+ return [4 /*yield*/, sdk.api.endusers.createOne({
1176
+ fname: 'Private',
1177
+ lname: 'Test',
1178
+ email: testEmail + '.source',
1179
+ eligibleForAutoMerge: true, // Manually set to test that private submission ignores it
1180
+ })];
1181
+ case 2:
1182
+ source = _b.sent();
1183
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)
1184
+ // Create form response via admin SDK (private/non-public submission)
1185
+ ];
1186
+ case 3:
1187
+ _a = _b.sent(), form = _a.form, fields = _a.fields;
1188
+ fnameField = fields.find(function (f) { return f.intakeField === 'fname'; });
1189
+ lnameField = fields.find(function (f) { return f.intakeField === 'lname'; });
1190
+ emailField = fields.find(function (f) { return f.intakeField === 'email'; });
1191
+ return [4 /*yield*/, sdk.api.form_responses.createOne({
1192
+ formId: form.id,
1193
+ formTitle: form.title,
1194
+ enduserId: source.id,
1195
+ responses: [
1196
+ { fieldId: fnameField.id, fieldTitle: fnameField.title, answer: { type: 'string', value: 'Private' } },
1197
+ { fieldId: lnameField.id, fieldTitle: lnameField.title, answer: { type: 'string', value: 'Test' } },
1198
+ { fieldId: emailField.id, fieldTitle: emailField.title, answer: { type: 'email', value: testEmail } },
1199
+ ],
1200
+ })
1201
+ // Source should NOT be deleted because this was a private submission (not publicSubmit)
1202
+ ];
1203
+ case 4:
1204
+ _b.sent();
1205
+ return [4 /*yield*/, isEnduserDeleted(sdk, source.id)];
1206
+ case 5:
1207
+ sourceStillExists = !(_b.sent());
1208
+ // Cleanup
1209
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
1210
+ case 6:
1211
+ // Cleanup
1212
+ _b.sent();
1213
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
1214
+ case 7:
1215
+ _b.sent();
1216
+ if (!sourceStillExists) return [3 /*break*/, 9];
1217
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(source.id)];
1218
+ case 8:
1219
+ _b.sent();
1220
+ _b.label = 9;
1221
+ case 9: return [2 /*return*/, sourceStillExists];
1222
+ }
1223
+ });
1224
+ }); }, { expectedResult: true })
1225
+ // Test 17: OR logic - matches on email even when phone differs
1226
+ ];
1227
+ case 16:
1228
+ // Test 16: Backwards compat - Private form submission doesn't trigger auto-merge
1229
+ _b.sent();
1230
+ // Test 17: OR logic - matches on email even when phone differs
1231
+ return [4 /*yield*/, async_test("Auto-merge: Merge occurs when email matches even if phone differs (OR logic)", function () { return __awaiter(void 0, void 0, void 0, function () {
1232
+ var testEmail, destination, _a, form, fields, sourceId, sourceDeleted, updatedDestination;
1233
+ var _b;
1234
+ return __generator(this, function (_c) {
1235
+ switch (_c.label) {
1236
+ case 0:
1237
+ testEmail = "automerge.orlogic.".concat(Date.now(), "@test.com");
1238
+ return [4 /*yield*/, sdk.api.endusers.createOne({
1239
+ fname: 'OrLogic',
1240
+ lname: 'Test',
1241
+ email: testEmail,
1242
+ phone: '+15555550001', // Different phone
1243
+ })];
1244
+ case 1:
1245
+ destination = _c.sent();
1246
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)
1247
+ // Submit with matching email but DIFFERENT phone
1248
+ ];
1249
+ case 2:
1250
+ _a = _c.sent(), form = _a.form, fields = _a.fields;
1251
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
1252
+ fname: 'OrLogic',
1253
+ lname: 'Test',
1254
+ email: testEmail,
1255
+ phone: '+15555550002', // Different phone than destination
1256
+ })
1257
+ // Should merge because email matches (OR logic, not AND)
1258
+ ];
1259
+ case 3:
1260
+ sourceId = (_c.sent()).enduserId;
1261
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)];
1262
+ case 4:
1263
+ sourceDeleted = _c.sent();
1264
+ return [4 /*yield*/, sdk.api.endusers.getOne(destination.id)
1265
+ // Cleanup
1266
+ ];
1267
+ case 5:
1268
+ updatedDestination = _c.sent();
1269
+ // Cleanup
1270
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
1271
+ case 6:
1272
+ // Cleanup
1273
+ _c.sent();
1274
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
1275
+ case 7:
1276
+ _c.sent();
1277
+ return [2 /*return*/, sourceDeleted && ((_b = updatedDestination.mergedIds) === null || _b === void 0 ? void 0 : _b.includes(sourceId))];
1278
+ }
1279
+ });
1280
+ }); }, { expectedResult: true })
1281
+ // Test 18: Partial name mismatch - fname matches but lname differs
1282
+ ];
1283
+ case 17:
1284
+ // Test 17: OR logic - matches on email even when phone differs
1285
+ _b.sent();
1286
+ // Test 18: Partial name mismatch - fname matches but lname differs
1287
+ return [4 /*yield*/, async_test("Auto-merge: No merge when fname matches but lname differs", function () { return __awaiter(void 0, void 0, void 0, function () {
1288
+ var testEmail, destination, _a, form, fields, sourceId, sourceDeleted;
1289
+ return __generator(this, function (_b) {
1290
+ switch (_b.label) {
1291
+ case 0:
1292
+ testEmail = "automerge.partial.".concat(Date.now(), "@test.com");
1293
+ return [4 /*yield*/, sdk.api.endusers.createOne({
1294
+ fname: 'Partial',
1295
+ lname: 'Match',
1296
+ email: testEmail
1297
+ })];
1298
+ case 1:
1299
+ destination = _b.sent();
1300
+ return [4 /*yield*/, createAutoMergeForm(sdk, true)
1301
+ // Submit with same fname but DIFFERENT lname
1302
+ ];
1303
+ case 2:
1304
+ _a = _b.sent(), form = _a.form, fields = _a.fields;
1305
+ return [4 /*yield*/, submitPublicFormWithSkipMatch(form, fields, {
1306
+ fname: 'Partial',
1307
+ lname: 'Different',
1308
+ email: testEmail, // Same email
1309
+ })
1310
+ // Should NOT merge because lname differs
1311
+ ];
1312
+ case 3:
1313
+ sourceId = (_b.sent()).enduserId;
1314
+ return [4 /*yield*/, isEnduserDeleted(sdk, sourceId)
1315
+ // Cleanup
1316
+ ];
1317
+ case 4:
1318
+ sourceDeleted = _b.sent();
1319
+ // Cleanup
1320
+ return [4 /*yield*/, sdk.api.forms.deleteOne(form.id)];
1321
+ case 5:
1322
+ // Cleanup
1323
+ _b.sent();
1324
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(destination.id)];
1325
+ case 6:
1326
+ _b.sent();
1327
+ if (!!sourceDeleted) return [3 /*break*/, 8];
1328
+ return [4 /*yield*/, sdk.api.endusers.deleteOne(sourceId)];
1329
+ case 7:
1330
+ _b.sent();
1331
+ _b.label = 8;
1332
+ case 8: return [2 /*return*/, !sourceDeleted]; // Source should NOT be deleted
1333
+ }
1334
+ });
1335
+ }); }, { expectedResult: true })];
1336
+ case 18:
1337
+ // Test 18: Partial name mismatch - fname matches but lname differs
1338
+ _b.sent();
1339
+ return [2 /*return*/];
1340
+ }
1341
+ });
1342
+ });
1343
+ };
1344
+ // Allow running this test file independently
1345
+ if (require.main === module) {
1346
+ console.log("Using API URL: ".concat(host));
1347
+ var sdk_1 = new Session({ host: host });
1348
+ var sdkNonAdmin_1 = new Session({ host: host });
1349
+ var runTests = function () { return __awaiter(void 0, void 0, void 0, function () {
1350
+ return __generator(this, function (_a) {
1351
+ switch (_a.label) {
1352
+ case 0: return [4 /*yield*/, setup_tests(sdk_1, sdkNonAdmin_1)];
1353
+ case 1:
1354
+ _a.sent();
1355
+ return [4 /*yield*/, auto_merge_form_submission_tests({ sdk: sdk_1, sdkNonAdmin: sdkNonAdmin_1 })];
1356
+ case 2:
1357
+ _a.sent();
1358
+ return [2 /*return*/];
1359
+ }
1360
+ });
1361
+ }); };
1362
+ runTests()
1363
+ .then(function () {
1364
+ console.log("Auto-merge form submission test suite completed successfully");
1365
+ process.exit(0);
1366
+ })
1367
+ .catch(function (error) {
1368
+ console.error("Auto-merge form submission test suite failed:", error);
1369
+ process.exit(1);
1370
+ });
1371
+ }
1372
+ //# sourceMappingURL=auto_merge_form_submission.test.js.map