@pdtf/schemas 3.4.0-9 → 3.4.1-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/.claude/settings.local.json +15 -0
- package/README.md +326 -6
- package/index.js +131 -0
- package/package.json +1 -1
- package/src/examples/v3/exampleTransaction.json +5 -1
- package/src/schemas/v3/combined.json +8 -21
- package/src/schemas/v3/overlays/README.md +299 -0
- package/src/schemas/v3/overlays/extensions/as.json +57 -0
- package/src/schemas/v3/overlays/extensions/dr.json +60 -0
- package/src/schemas/v3/overlays/extensions/er.json +71 -0
- package/src/schemas/v3/overlays/extensions/fd.json +63 -0
- package/src/schemas/v3/overlays/extensions/hi.json +44 -0
- package/src/schemas/v3/overlays/extensions/hs.json +56 -0
- package/src/schemas/v3/overlays/extensions/jk.json +55 -0
- package/src/schemas/v3/overlays/extensions/la.json +66 -0
- package/src/schemas/v3/overlays/extensions/ma.json +87 -0
- package/src/schemas/v3/overlays/extensions/mc.json +36 -0
- package/src/schemas/v3/overlays/extensions/oa.json +22 -0
- package/src/schemas/v3/overlays/extensions/oc.json +35 -0
- package/src/schemas/v3/overlays/extensions/sb.json +56 -0
- package/src/schemas/v3/overlays/extensions/sf.json +57 -0
- package/src/schemas/v3/overlays/extensions/sl.json +46 -0
- package/src/schemas/v3/overlays/extensions/tf.json +91 -0
- package/src/schemas/v3/overlays/nts2.json +6 -18
- package/src/schemas/v3/overlays/ntsl2.json +2 -8
- package/src/schemas/v3/overlays/ta6.json +1 -0
- package/src/schemas/v3/property-transaction-milestones.json +91 -0
- package/src/tests/v3/extensionOverlays.test.js +397 -0
- package/src/utils/extractExtensionOverlays.js +318 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Transaction Milestones",
|
|
4
|
+
"description": "Schema for tracking transaction milestones throughout the conveyancing process",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"listed": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"completed": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"format": "date-time"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"legalForms": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"completed": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"format": "date-time"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"soldSubjectToContract": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {
|
|
28
|
+
"completed": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"format": "date-time"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"searches": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"properties": {
|
|
37
|
+
"ordered": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"format": "date-time"
|
|
40
|
+
},
|
|
41
|
+
"expected": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"format": "date-time"
|
|
44
|
+
},
|
|
45
|
+
"completed": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"format": "date-time"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"enquiries": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"completed": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"format": "date-time"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"exchangeOfContracts": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"properties": {
|
|
63
|
+
"expected": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"format": "date-time"
|
|
66
|
+
},
|
|
67
|
+
"completed": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"format": "date-time"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"completion": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"properties": {
|
|
76
|
+
"started": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"format": "date-time"
|
|
79
|
+
},
|
|
80
|
+
"expected": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"format": "date-time"
|
|
83
|
+
},
|
|
84
|
+
"completed": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"format": "date-time"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
const {
|
|
2
|
+
getTransactionSchema,
|
|
3
|
+
extensionOverlays,
|
|
4
|
+
getValidator,
|
|
5
|
+
} = require("../../../index.js");
|
|
6
|
+
|
|
7
|
+
const schemaId =
|
|
8
|
+
"https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json";
|
|
9
|
+
|
|
10
|
+
describe("Extension Overlays", () => {
|
|
11
|
+
describe("String key support", () => {
|
|
12
|
+
test("should load extension overlays using string keys", () => {
|
|
13
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "jk"]);
|
|
14
|
+
const jkProp =
|
|
15
|
+
schema.properties?.propertyPack?.properties?.specialistIssues
|
|
16
|
+
?.properties?.japaneseKnotweed;
|
|
17
|
+
|
|
18
|
+
expect(jkProp).toBeDefined();
|
|
19
|
+
expect(jkProp.ntsRef).toBe("A5.3");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("should work with multiple extension string keys", () => {
|
|
23
|
+
const schema = getTransactionSchema(schemaId, [
|
|
24
|
+
"nts2023",
|
|
25
|
+
"jk",
|
|
26
|
+
"tf",
|
|
27
|
+
"ma",
|
|
28
|
+
"er",
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
const jkProp =
|
|
32
|
+
schema.properties?.propertyPack?.properties?.specialistIssues
|
|
33
|
+
?.properties?.japaneseKnotweed;
|
|
34
|
+
const tfProp =
|
|
35
|
+
schema.properties?.propertyPack?.properties?.ownership?.properties
|
|
36
|
+
?.ownershipsToBeTransferred?.items?.oneOf?.[2]?.properties
|
|
37
|
+
?.leaseholdInformation?.properties?.serviceCharge?.oneOf?.[1]
|
|
38
|
+
?.properties?.transferFees;
|
|
39
|
+
const maProp =
|
|
40
|
+
schema.properties?.propertyPack?.properties?.ownership?.properties
|
|
41
|
+
?.ownershipsToBeTransferred?.items?.oneOf?.[2]?.properties
|
|
42
|
+
?.leaseholdInformation?.properties?.contactDetails;
|
|
43
|
+
const erProp =
|
|
44
|
+
schema.properties?.propertyPack?.properties?.ownership?.properties
|
|
45
|
+
?.ownershipsToBeTransferred?.items?.oneOf?.[0]?.properties
|
|
46
|
+
?.estateRentcharges;
|
|
47
|
+
|
|
48
|
+
expect(jkProp?.ntsRef).toBe("A5.3");
|
|
49
|
+
expect(tfProp?.ntsRef).toBe("A3.5.1.1");
|
|
50
|
+
expect(maProp?.ntsRef).toBe("A1.5.4");
|
|
51
|
+
expect(erProp?.ntsRef).toBe("A3.4");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("should be equivalent to direct object access", () => {
|
|
55
|
+
const schemaString = getTransactionSchema(schemaId, ["nts2023", "jk"]);
|
|
56
|
+
const schemaDirect = getTransactionSchema(schemaId, [
|
|
57
|
+
"nts2023",
|
|
58
|
+
extensionOverlays.jk,
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
expect(schemaString).toEqual(schemaDirect);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("Individual extension functionality", () => {
|
|
66
|
+
test("should add Japanese Knotweed extension correctly", () => {
|
|
67
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "jk"]);
|
|
68
|
+
const jkProp =
|
|
69
|
+
schema.properties?.propertyPack?.properties?.specialistIssues
|
|
70
|
+
?.properties?.japaneseKnotweed;
|
|
71
|
+
|
|
72
|
+
expect(jkProp).toBeDefined();
|
|
73
|
+
expect(jkProp.ntsRef).toBe("A5.3");
|
|
74
|
+
expect(jkProp.required).toContain("yesNo");
|
|
75
|
+
expect(jkProp.discriminator?.propertyName).toBe("yesNo");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("should add Transfer Fees extension correctly", () => {
|
|
79
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "tf"]);
|
|
80
|
+
const tfProp =
|
|
81
|
+
schema.properties?.propertyPack?.properties?.ownership?.properties
|
|
82
|
+
?.ownershipsToBeTransferred?.items?.oneOf?.[2]?.properties
|
|
83
|
+
?.leaseholdInformation?.properties?.serviceCharge?.oneOf?.[1]
|
|
84
|
+
?.properties?.transferFees;
|
|
85
|
+
|
|
86
|
+
expect(tfProp).toBeDefined();
|
|
87
|
+
expect(tfProp.ntsRef).toBe("A3.5.1.1");
|
|
88
|
+
expect(tfProp.title).toBe(
|
|
89
|
+
"Are there any additional fees payable on sale or letting?"
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should add Managing Agent extension correctly", () => {
|
|
94
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "ma"]);
|
|
95
|
+
const maProp =
|
|
96
|
+
schema.properties?.propertyPack?.properties?.ownership?.properties
|
|
97
|
+
?.ownershipsToBeTransferred?.items?.oneOf?.[2]?.properties
|
|
98
|
+
?.leaseholdInformation?.properties?.contactDetails;
|
|
99
|
+
|
|
100
|
+
expect(maProp).toBeDefined();
|
|
101
|
+
expect(maProp.ntsRef).toBe("A1.5.4");
|
|
102
|
+
expect(maProp.required).toContain("contacts");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("should add Solar Panels extension correctly", () => {
|
|
106
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "sl"]);
|
|
107
|
+
const slProp =
|
|
108
|
+
schema.properties?.propertyPack?.properties?.electricity?.properties
|
|
109
|
+
?.solarPanels?.oneOf?.[1]?.properties?.panelsOwnedOutright;
|
|
110
|
+
|
|
111
|
+
expect(slProp).toBeDefined();
|
|
112
|
+
expect(slProp.ntsRef).toBe("B3.7.1");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("should add Heating Installation extension correctly", () => {
|
|
116
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "hi"]);
|
|
117
|
+
const hiProp =
|
|
118
|
+
schema.properties?.propertyPack?.properties?.heating?.properties
|
|
119
|
+
?.heatingSystem?.oneOf?.[1]?.properties?.centralHeatingDetails
|
|
120
|
+
?.properties?.centralHeatingInstalled;
|
|
121
|
+
|
|
122
|
+
expect(hiProp).toBeDefined();
|
|
123
|
+
expect(hiProp.ntsRef).toBe("B3.4.3.2");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("should add Estate Rentcharges extension correctly", () => {
|
|
127
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "er"]);
|
|
128
|
+
const erProp =
|
|
129
|
+
schema.properties?.propertyPack?.properties?.ownership?.properties
|
|
130
|
+
?.ownershipsToBeTransferred?.items?.oneOf?.[0]?.properties
|
|
131
|
+
?.estateRentcharges;
|
|
132
|
+
|
|
133
|
+
expect(erProp).toBeDefined();
|
|
134
|
+
expect(erProp.ntsRef).toBe("A3.4");
|
|
135
|
+
expect(erProp.required).toContain("yesNo");
|
|
136
|
+
expect(erProp.discriminator?.propertyName).toBe("yesNo");
|
|
137
|
+
expect(erProp.oneOf).toBeDefined();
|
|
138
|
+
expect(Array.isArray(erProp.oneOf)).toBe(true);
|
|
139
|
+
expect(erProp.oneOf.length).toBeGreaterThan(0);
|
|
140
|
+
|
|
141
|
+
// Check the conditional branch for "Yes" response
|
|
142
|
+
const yesBranch = erProp.oneOf.find(
|
|
143
|
+
(branch) =>
|
|
144
|
+
branch.required?.includes("details") &&
|
|
145
|
+
branch.required?.includes("amount")
|
|
146
|
+
);
|
|
147
|
+
expect(yesBranch).toBeDefined();
|
|
148
|
+
expect(yesBranch.properties?.details?.ntsRef).toBe("A3.4.2");
|
|
149
|
+
expect(yesBranch.properties?.amount?.ntsRef).toBe("A3.4.3");
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("Extension overlay availability", () => {
|
|
154
|
+
test("should export all extension overlays", () => {
|
|
155
|
+
const expectedKeys = [
|
|
156
|
+
"as",
|
|
157
|
+
"dr",
|
|
158
|
+
"jk",
|
|
159
|
+
"sb",
|
|
160
|
+
"hs", // Specialist Issues
|
|
161
|
+
"oa",
|
|
162
|
+
"la",
|
|
163
|
+
"sf",
|
|
164
|
+
"mc", // Property Features
|
|
165
|
+
"er",
|
|
166
|
+
"ma",
|
|
167
|
+
"tf", // Ownership & Financial
|
|
168
|
+
"sl",
|
|
169
|
+
"hi",
|
|
170
|
+
"fd", // Utilities & Services
|
|
171
|
+
"oc", // Transaction
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
expect(Object.keys(extensionOverlays).sort()).toEqual(
|
|
175
|
+
expectedKeys.sort()
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("should have valid JSON structure for all extensions", () => {
|
|
180
|
+
Object.entries(extensionOverlays).forEach(([key, overlay]) => {
|
|
181
|
+
expect(overlay).toBeDefined();
|
|
182
|
+
expect(overlay.$schema).toBe("http://json-schema.org/draft-07/schema#");
|
|
183
|
+
expect(overlay.$id).toContain(`extensions/${key}.json`);
|
|
184
|
+
expect(overlay.properties).toBeDefined();
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("NTS2 equivalence test", () => {
|
|
190
|
+
test("should produce equivalent result to nts2 when all relevant extensions are combined", () => {
|
|
191
|
+
// Get the full NTS2 schema
|
|
192
|
+
const nts2Schema = getTransactionSchema(schemaId, ["nts2025"]);
|
|
193
|
+
|
|
194
|
+
// Get NTS base with all relevant extensions
|
|
195
|
+
const allExtensions = [
|
|
196
|
+
"nts2023",
|
|
197
|
+
// Specialist issues extensions
|
|
198
|
+
"as",
|
|
199
|
+
"dr",
|
|
200
|
+
"jk",
|
|
201
|
+
"sb",
|
|
202
|
+
"hs",
|
|
203
|
+
// Property features extensions
|
|
204
|
+
"oa",
|
|
205
|
+
"la",
|
|
206
|
+
"sf",
|
|
207
|
+
"mc",
|
|
208
|
+
// Ownership & financial extensions
|
|
209
|
+
"er",
|
|
210
|
+
"ma",
|
|
211
|
+
"tf",
|
|
212
|
+
// Utilities & services extensions
|
|
213
|
+
"sl",
|
|
214
|
+
"hi",
|
|
215
|
+
"fd",
|
|
216
|
+
// Transaction extensions
|
|
217
|
+
"oc",
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
const ntsWithAllExtensions = getTransactionSchema(
|
|
221
|
+
schemaId,
|
|
222
|
+
allExtensions
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Compare key structural elements
|
|
226
|
+
const nts2PropPack = nts2Schema.properties?.propertyPack;
|
|
227
|
+
const extPropPack = ntsWithAllExtensions.properties?.propertyPack;
|
|
228
|
+
|
|
229
|
+
// Check that specialist issues section exists in both
|
|
230
|
+
expect(nts2PropPack?.properties?.specialistIssues).toBeDefined();
|
|
231
|
+
expect(extPropPack?.properties?.specialistIssues).toBeDefined();
|
|
232
|
+
|
|
233
|
+
// Check key specialist issues properties
|
|
234
|
+
const nts2SI = nts2PropPack.properties.specialistIssues.properties;
|
|
235
|
+
const extSI = extPropPack.properties.specialistIssues.properties;
|
|
236
|
+
|
|
237
|
+
expect(extSI?.japaneseKnotweed).toBeDefined();
|
|
238
|
+
expect(extSI?.dryRotEtcTreatment).toBeDefined();
|
|
239
|
+
expect(extSI?.containsAsbestos).toBeDefined();
|
|
240
|
+
expect(extSI?.subsidenceOrStructuralFault).toBeDefined();
|
|
241
|
+
expect(extSI?.ongoingHealthOrSafetyIssue).toBeDefined();
|
|
242
|
+
|
|
243
|
+
// Check that references match (extensions use ntsRef, NTS2 uses nts2Ref)
|
|
244
|
+
if (nts2SI?.japaneseKnotweed && extSI?.japaneseKnotweed) {
|
|
245
|
+
expect(extSI.japaneseKnotweed.ntsRef).toBe("A5.3");
|
|
246
|
+
// NTS2 might have nts2Ref instead - this is expected difference
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Check outside areas
|
|
250
|
+
expect(
|
|
251
|
+
extPropPack?.properties?.residentialPropertyFeatures?.properties
|
|
252
|
+
?.outsideAreas
|
|
253
|
+
).toBeDefined();
|
|
254
|
+
|
|
255
|
+
// Check leasehold extensions
|
|
256
|
+
const leaseholdInfo =
|
|
257
|
+
extPropPack?.properties?.ownership?.properties
|
|
258
|
+
?.ownershipsToBeTransferred?.items?.oneOf?.[2]?.properties
|
|
259
|
+
?.leaseholdInformation;
|
|
260
|
+
expect(leaseholdInfo?.properties?.contactDetails).toBeDefined(); // Managing agent
|
|
261
|
+
expect(
|
|
262
|
+
leaseholdInfo?.properties?.serviceCharge?.oneOf?.[1]?.properties
|
|
263
|
+
?.transferFees
|
|
264
|
+
).toBeDefined(); // Transfer fees
|
|
265
|
+
|
|
266
|
+
// Check utility extensions
|
|
267
|
+
expect(
|
|
268
|
+
extPropPack?.properties?.electricity?.properties?.solarPanels
|
|
269
|
+
?.oneOf?.[1]?.properties?.panelsOwnedOutright
|
|
270
|
+
).toBeDefined();
|
|
271
|
+
expect(
|
|
272
|
+
extPropPack?.properties?.heating?.properties?.heatingSystem?.oneOf?.[1]
|
|
273
|
+
?.properties?.centralHeatingDetails?.properties
|
|
274
|
+
?.centralHeatingInstalled
|
|
275
|
+
).toBeDefined();
|
|
276
|
+
|
|
277
|
+
// Check construction extensions
|
|
278
|
+
expect(
|
|
279
|
+
extPropPack?.properties?.typeOfConstruction?.properties?.loft
|
|
280
|
+
).toBeDefined();
|
|
281
|
+
expect(
|
|
282
|
+
extPropPack?.properties?.typeOfConstruction?.properties
|
|
283
|
+
?.sprayFoamInsulation
|
|
284
|
+
).toBeDefined();
|
|
285
|
+
|
|
286
|
+
// Check completion and moving
|
|
287
|
+
expect(
|
|
288
|
+
extPropPack?.properties?.completionAndMoving?.properties
|
|
289
|
+
?.otherPropertyInChain
|
|
290
|
+
).toBeDefined();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("should validate data successfully with all extensions", () => {
|
|
294
|
+
const allExtensions = [
|
|
295
|
+
"nts2023",
|
|
296
|
+
"as",
|
|
297
|
+
"dr",
|
|
298
|
+
"jk",
|
|
299
|
+
"sb",
|
|
300
|
+
"hs",
|
|
301
|
+
"oa",
|
|
302
|
+
"la",
|
|
303
|
+
"sf",
|
|
304
|
+
"mc",
|
|
305
|
+
"er",
|
|
306
|
+
"ma",
|
|
307
|
+
"tf",
|
|
308
|
+
"sl",
|
|
309
|
+
"hi",
|
|
310
|
+
"fd",
|
|
311
|
+
"oc",
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
const validator = getValidator(schemaId, allExtensions);
|
|
315
|
+
expect(validator).toBeDefined();
|
|
316
|
+
expect(typeof validator).toBe("function");
|
|
317
|
+
|
|
318
|
+
// Test with minimal valid data structure
|
|
319
|
+
const testData = {
|
|
320
|
+
propertyPack: {
|
|
321
|
+
priceInformation: { price: 100000, priceQualifier: "Freehold" },
|
|
322
|
+
ownership: { ownershipsToBeTransferred: [] },
|
|
323
|
+
councilTax: { band: "A" },
|
|
324
|
+
energyEfficiency: { epcRating: "C" },
|
|
325
|
+
buildInformation: { building: { buildDate: 2000 } },
|
|
326
|
+
typeOfConstruction: { isStandardForm: { yesNo: "Yes" } },
|
|
327
|
+
electricity: { mainsElectricity: { yesNo: "Yes" } },
|
|
328
|
+
connectivity: { mobilePhoneCoverage: { signal: "Good" } },
|
|
329
|
+
waterAndDrainage: { water: { supplier: "Thames Water" } },
|
|
330
|
+
heating: { heatingSystem: { yesNo: "Yes" } },
|
|
331
|
+
parking: { parkingArrangements: { yesNo: "No" } },
|
|
332
|
+
listingAndConservation: { listedBuilding: { yesNo: "No" } },
|
|
333
|
+
rightsAndInformalArrangements: { rightsGranted: { yesNo: "No" } },
|
|
334
|
+
environmentalIssues: { flooding: { yesNo: "No" } },
|
|
335
|
+
notices: { noticesAndProposals: { yesNo: "No" } },
|
|
336
|
+
specialistIssues: {
|
|
337
|
+
japaneseKnotweed: { yesNo: "No" },
|
|
338
|
+
dryRotEtcTreatment: { yesNo: "No" },
|
|
339
|
+
containsAsbestos: { yesNo: "No" },
|
|
340
|
+
subsidenceOrStructuralFault: { yesNo: "No" },
|
|
341
|
+
ongoingHealthOrSafetyIssue: { yesNo: "No" },
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// This might not validate due to missing required fields, but validator should not throw
|
|
347
|
+
expect(() => validator(testData)).not.toThrow();
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
describe("Extension merging behavior", () => {
|
|
352
|
+
test("should preserve required arrays when merging extensions", () => {
|
|
353
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "jk", "as"]);
|
|
354
|
+
const siSection =
|
|
355
|
+
schema.properties?.propertyPack?.properties?.specialistIssues;
|
|
356
|
+
|
|
357
|
+
expect(siSection?.required).toBeDefined();
|
|
358
|
+
expect(siSection.required).toContain("japaneseKnotweed");
|
|
359
|
+
expect(siSection.required).toContain("containsAsbestos");
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("should handle discriminator patterns correctly", () => {
|
|
363
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "jk"]);
|
|
364
|
+
const jkProp =
|
|
365
|
+
schema.properties?.propertyPack?.properties?.specialistIssues
|
|
366
|
+
?.properties?.japaneseKnotweed;
|
|
367
|
+
|
|
368
|
+
expect(jkProp?.discriminator).toBeDefined();
|
|
369
|
+
expect(jkProp.discriminator.propertyName).toBe("yesNo");
|
|
370
|
+
expect(jkProp.oneOf).toBeDefined();
|
|
371
|
+
expect(Array.isArray(jkProp.oneOf)).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("should merge oneOf schemas correctly", () => {
|
|
375
|
+
const schema = getTransactionSchema(schemaId, ["nts2023", "tf"]);
|
|
376
|
+
const tfProp =
|
|
377
|
+
schema.properties?.propertyPack?.properties?.ownership?.properties
|
|
378
|
+
?.ownershipsToBeTransferred?.items?.oneOf?.[2]?.properties
|
|
379
|
+
?.leaseholdInformation?.properties?.serviceCharge?.oneOf?.[1]
|
|
380
|
+
?.properties?.transferFees;
|
|
381
|
+
|
|
382
|
+
expect(tfProp?.oneOf).toBeDefined();
|
|
383
|
+
expect(tfProp.oneOf.length).toBeGreaterThan(0);
|
|
384
|
+
|
|
385
|
+
// Check the structure - details should be in the conditional oneOf branch
|
|
386
|
+
const conditionalBranch = tfProp.oneOf.find(
|
|
387
|
+
(branch) =>
|
|
388
|
+
branch.properties?.details || branch.required?.includes("details")
|
|
389
|
+
);
|
|
390
|
+
expect(conditionalBranch).toBeDefined();
|
|
391
|
+
expect(
|
|
392
|
+
conditionalBranch.properties?.details ||
|
|
393
|
+
conditionalBranch.required?.includes("details")
|
|
394
|
+
).toBeTruthy();
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
});
|