@legalplace/prerenderx 3.8.24 → 3.9.0

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.
@@ -0,0 +1,278 @@
1
+ import type { ModelV3 } from "@legalplace/models-v3-types";
2
+ import Prerender from "../src/libs/Prerender";
3
+
4
+ /**
5
+ * Builds a Model V3 with one document `main` configured as multiple
6
+ * (`params.multiple.enabled = true`, source = option #2). The output
7
+ * `[var:3]` interpolates the partner name at each occurrence.
8
+ */
9
+ const buildMultipleDocumentModel = (
10
+ filenameTemplate?: string
11
+ ): ModelV3 => ({
12
+ documents: {
13
+ main: {
14
+ name: "Contract",
15
+ sections: [
16
+ {
17
+ id: 1,
18
+ label: "Section",
19
+ options: [2, 5],
20
+ },
21
+ ],
22
+ params: {
23
+ multiple: {
24
+ enabled: true,
25
+ repeatOption: 2,
26
+ ...(filenameTemplate ? { filenameTemplate } : {}),
27
+ },
28
+ },
29
+ },
30
+ },
31
+ options: {
32
+ "2": {
33
+ meta: {
34
+ id: 2,
35
+ type: "static",
36
+ label: "Partner",
37
+ step: "*",
38
+ multiple: {
39
+ enabled: true,
40
+ incrementationStart: 1,
41
+ },
42
+ },
43
+ options: [],
44
+ variables: [3],
45
+ },
46
+ "5": {
47
+ meta: {
48
+ id: 5,
49
+ type: "hidden",
50
+ label: "Body",
51
+ step: "*",
52
+ output: "<p>Hello [var:3]</p>",
53
+ },
54
+ options: [],
55
+ variables: [3],
56
+ },
57
+ },
58
+ variables: {
59
+ "3": {
60
+ id: 3,
61
+ type: "text",
62
+ label: "Partner name",
63
+ step: "*",
64
+ },
65
+ },
66
+ customization: {},
67
+ });
68
+
69
+ const ovcWithThreePartners = {
70
+ options: {
71
+ "2": [true, true, true],
72
+ },
73
+ variables: {
74
+ "3": ["Alice", "Bob", "Charlie"],
75
+ },
76
+ };
77
+
78
+ describe("Prerender — document multiple", () => {
79
+ it("emits one entry per occurrence with distinct hashes", () => {
80
+ const prerender = new Prerender(
81
+ buildMultipleDocumentModel(),
82
+ ovcWithThreePartners
83
+ );
84
+
85
+ const index = prerender.getDocumentsIndex();
86
+ const entries = Object.entries(index);
87
+
88
+ expect(entries).toHaveLength(3);
89
+ const slugs = entries.map(([, e]) => e.slug);
90
+ expect(slugs).toEqual(["main", "main", "main"]);
91
+
92
+ const occurrenceIndexes = entries.map(([, e]) => e.occurrenceIndex);
93
+ expect(occurrenceIndexes.sort()).toEqual([0, 1, 2]);
94
+
95
+ // Hashes must be distinct across occurrences
96
+ const hashes = entries.map(([h]) => h);
97
+ expect(new Set(hashes).size).toBe(hashes.length);
98
+ });
99
+
100
+ it("uses the document title for each occurrence when no template is set", () => {
101
+ const prerender = new Prerender(
102
+ buildMultipleDocumentModel(),
103
+ ovcWithThreePartners
104
+ );
105
+ const index = prerender.getDocumentsIndex();
106
+ const names = Object.values(index)
107
+ .map((e) => e.name)
108
+ .sort();
109
+
110
+ expect(names).toEqual(["Contract", "Contract", "Contract"]);
111
+ });
112
+
113
+ it("interpolates document name with variables at each occurrence", () => {
114
+ const model = buildMultipleDocumentModel();
115
+ model.documents.main.name = "Contract - [var:3]";
116
+ if (model.documents.main.params?.multiple) {
117
+ delete model.documents.main.params.multiple.filenameTemplate;
118
+ }
119
+
120
+ const prerender = new Prerender(model, ovcWithThreePartners);
121
+ const index = prerender.getDocumentsIndex();
122
+ const names = Object.values(index)
123
+ .map((e) => e.name)
124
+ .sort();
125
+
126
+ expect(names).toEqual([
127
+ "Contract - Alice",
128
+ "Contract - Bob",
129
+ "Contract - Charlie",
130
+ ]);
131
+ });
132
+
133
+ it("interpolates filenameTemplate with the variable value at each occurrence", () => {
134
+ const prerender = new Prerender(
135
+ buildMultipleDocumentModel("Contract - [var:3]"),
136
+ ovcWithThreePartners
137
+ );
138
+ const index = prerender.getDocumentsIndex();
139
+ const names = Object.values(index)
140
+ .map((e) => e.name)
141
+ .sort();
142
+
143
+ expect(names).toEqual([
144
+ "Contract - Alice",
145
+ "Contract - Bob",
146
+ "Contract - Charlie",
147
+ ]);
148
+ });
149
+
150
+ it("renders the body with the right variable for each occurrence", () => {
151
+ const prerender = new Prerender(
152
+ buildMultipleDocumentModel(),
153
+ ovcWithThreePartners
154
+ );
155
+ const index = prerender.getDocumentsIndex();
156
+
157
+ const entries = Object.entries(index).sort(
158
+ (a, b) =>
159
+ ((a[1].occurrenceIndex ?? 0) as number) -
160
+ ((b[1].occurrenceIndex ?? 0) as number)
161
+ );
162
+
163
+ const rendered = entries.map(([hash]) =>
164
+ prerender.getDocument(hash) as string
165
+ );
166
+
167
+ expect(rendered[0]).toContain("Hello Alice");
168
+ expect(rendered[1]).toContain("Hello Bob");
169
+ expect(rendered[2]).toContain("Hello Charlie");
170
+ });
171
+
172
+ it("returns a single entry when params.multiple.enabled is false", () => {
173
+ const model = buildMultipleDocumentModel();
174
+ if (model.documents.main.params?.multiple) {
175
+ model.documents.main.params.multiple.enabled = false;
176
+ }
177
+
178
+ const prerender = new Prerender(model, ovcWithThreePartners);
179
+ const index = prerender.getDocumentsIndex();
180
+
181
+ expect(Object.keys(index)).toHaveLength(1);
182
+ const [entry] = Object.values(index);
183
+ expect(entry.slug).toBe("main");
184
+ // No occurrenceIndex when not multiple
185
+ expect(entry.occurrenceIndex).toBeUndefined();
186
+ });
187
+
188
+ it("emits only related occurrences that pass a selected document condition", () => {
189
+ const model: ModelV3 = {
190
+ documents: {
191
+ main: {
192
+ name: "Main",
193
+ sections: [
194
+ {
195
+ id: 1,
196
+ label: "Section",
197
+ options: [2],
198
+ },
199
+ ],
200
+ },
201
+ annex: {
202
+ name: "Annex",
203
+ sections: [
204
+ {
205
+ id: 2,
206
+ label: "Annex section",
207
+ options: [],
208
+ },
209
+ ],
210
+ params: {
211
+ multiple: {
212
+ enabled: true,
213
+ repeatOption: 2,
214
+ },
215
+ conditions: {
216
+ selected: [{ var: "o.3" }],
217
+ scope: "related",
218
+ },
219
+ },
220
+ },
221
+ },
222
+ options: {
223
+ "2": {
224
+ meta: {
225
+ id: 2,
226
+ type: "static",
227
+ label: "Partner",
228
+ step: "*",
229
+ multiple: {
230
+ enabled: true,
231
+ incrementationStart: 1,
232
+ },
233
+ },
234
+ options: [3, 4],
235
+ variables: [],
236
+ },
237
+ "3": {
238
+ meta: {
239
+ id: 3,
240
+ type: "radio",
241
+ label: "Yes",
242
+ step: "*",
243
+ },
244
+ options: [],
245
+ variables: [],
246
+ },
247
+ "4": {
248
+ meta: {
249
+ id: 4,
250
+ type: "radio",
251
+ label: "No",
252
+ step: "*",
253
+ },
254
+ options: [],
255
+ variables: [],
256
+ },
257
+ },
258
+ variables: {},
259
+ customization: {},
260
+ };
261
+
262
+ const prerender = new Prerender(model, {
263
+ options: {
264
+ "2": [true, true],
265
+ "3": [true, false],
266
+ "4": [false, true],
267
+ },
268
+ variables: {},
269
+ });
270
+
271
+ const annexEntries = Object.values(prerender.getDocumentsIndex()).filter(
272
+ (entry) => entry.slug === "annex"
273
+ );
274
+
275
+ expect(annexEntries).toHaveLength(1);
276
+ expect(annexEntries[0].occurrenceIndex).toBe(0);
277
+ });
278
+ });
@@ -0,0 +1,115 @@
1
+ import type { ModelV3 } from "@legalplace/models-v3-types";
2
+ import Prerender from "../src/libs/Prerender";
3
+
4
+ /**
5
+ * Builds a Model V3 whose body is a `repeated` block linked to multiple
6
+ * option #2. The hidden child interpolates `[var:3]` at each occurrence.
7
+ *
8
+ * @param multipleType Type of the linked multiple source option
9
+ */
10
+ const buildRepeatedContentModel = (
11
+ multipleType: "static" | "checkbox" = "static"
12
+ ): ModelV3 => ({
13
+ documents: {
14
+ main: {
15
+ name: "Contract",
16
+ sections: [
17
+ {
18
+ id: 1,
19
+ label: "Section",
20
+ options: [10],
21
+ },
22
+ ],
23
+ },
24
+ },
25
+ options: {
26
+ "2": {
27
+ meta: {
28
+ id: 2,
29
+ type: multipleType,
30
+ label: "Partner",
31
+ step: "*",
32
+ multiple: {
33
+ enabled: true,
34
+ incrementationStart: 1,
35
+ },
36
+ },
37
+ options: [],
38
+ variables: [3],
39
+ },
40
+ "10": {
41
+ meta: {
42
+ id: 10,
43
+ type: "repeated",
44
+ label: "Repeated partners",
45
+ step: "*",
46
+ repeatOption: 2,
47
+ },
48
+ options: [11],
49
+ variables: [],
50
+ },
51
+ "11": {
52
+ meta: {
53
+ id: 11,
54
+ type: "hidden",
55
+ label: "Partner line",
56
+ step: "*",
57
+ output: "<p>Hello [var:3]</p>",
58
+ },
59
+ options: [],
60
+ variables: [3],
61
+ },
62
+ },
63
+ variables: {
64
+ "3": {
65
+ id: 3,
66
+ type: "text",
67
+ label: "Partner name",
68
+ step: "*",
69
+ },
70
+ },
71
+ customization: {},
72
+ });
73
+
74
+ const ovcWithThreePartners = {
75
+ options: {
76
+ "2": [true, true, true],
77
+ },
78
+ variables: {
79
+ "3": ["Alice", "Bob", "Charlie"],
80
+ },
81
+ };
82
+
83
+ describe("Prerender — repeated content", () => {
84
+ it("renders every occurrence of a repeated block", () => {
85
+ const prerender = new Prerender(
86
+ buildRepeatedContentModel(),
87
+ ovcWithThreePartners
88
+ );
89
+ const index = prerender.getDocumentsIndex();
90
+ const [hash] = Object.keys(index);
91
+ const rendered = prerender.getDocument(hash) as string;
92
+
93
+ expect(rendered).toContain("Hello Alice");
94
+ expect(rendered).toContain("Hello Bob");
95
+ expect(rendered).toContain("Hello Charlie");
96
+ });
97
+
98
+ it("skips inactive occurrences of the linked multiple", () => {
99
+ const prerender = new Prerender(buildRepeatedContentModel("checkbox"), {
100
+ options: {
101
+ "2": [true, false, true],
102
+ },
103
+ variables: {
104
+ "3": ["Alice", "Bob", "Charlie"],
105
+ },
106
+ });
107
+ const index = prerender.getDocumentsIndex();
108
+ const [hash] = Object.keys(index);
109
+ const rendered = prerender.getDocument(hash) as string;
110
+
111
+ expect(rendered).toContain("Hello Alice");
112
+ expect(rendered).not.toContain("Hello Bob");
113
+ expect(rendered).toContain("Hello Charlie");
114
+ });
115
+ });