@gisce/ooui 0.1.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.
Files changed (191) hide show
  1. package/README.md +10 -0
  2. package/dist/Binary.d.ts +14 -0
  3. package/dist/Binary.js +46 -0
  4. package/dist/Binary.js.map +1 -0
  5. package/dist/Boolean.d.ts +8 -0
  6. package/dist/Boolean.js +26 -0
  7. package/dist/Boolean.js.map +1 -0
  8. package/dist/Button.d.ts +29 -0
  9. package/dist/Button.js +108 -0
  10. package/dist/Button.js.map +1 -0
  11. package/dist/Char.d.ts +26 -0
  12. package/dist/Char.js +89 -0
  13. package/dist/Char.js.map +1 -0
  14. package/dist/Container.d.ts +44 -0
  15. package/dist/Container.js +148 -0
  16. package/dist/Container.js.map +1 -0
  17. package/dist/ContainerWidget.d.ts +42 -0
  18. package/dist/ContainerWidget.js +125 -0
  19. package/dist/ContainerWidget.js.map +1 -0
  20. package/dist/Date.d.ts +8 -0
  21. package/dist/Date.js +26 -0
  22. package/dist/Date.js.map +1 -0
  23. package/dist/DateTime.d.ts +8 -0
  24. package/dist/DateTime.js +26 -0
  25. package/dist/DateTime.js.map +1 -0
  26. package/dist/FiberGrid.d.ts +5 -0
  27. package/dist/FiberGrid.js +23 -0
  28. package/dist/FiberGrid.js.map +1 -0
  29. package/dist/Field.d.ts +59 -0
  30. package/dist/Field.js +162 -0
  31. package/dist/Field.js.map +1 -0
  32. package/dist/Float.d.ts +24 -0
  33. package/dist/Float.js +64 -0
  34. package/dist/Float.js.map +1 -0
  35. package/dist/FloatTime.d.ts +7 -0
  36. package/dist/FloatTime.js +26 -0
  37. package/dist/FloatTime.js.map +1 -0
  38. package/dist/Form.d.ts +53 -0
  39. package/dist/Form.js +199 -0
  40. package/dist/Form.js.map +1 -0
  41. package/dist/Group.d.ts +5 -0
  42. package/dist/Group.js +23 -0
  43. package/dist/Group.js.map +1 -0
  44. package/dist/Image.d.ts +7 -0
  45. package/dist/Image.js +26 -0
  46. package/dist/Image.js.map +1 -0
  47. package/dist/Integer.d.ts +8 -0
  48. package/dist/Integer.js +26 -0
  49. package/dist/Integer.js.map +1 -0
  50. package/dist/Label.d.ts +23 -0
  51. package/dist/Label.js +80 -0
  52. package/dist/Label.js.map +1 -0
  53. package/dist/Many2many.d.ts +26 -0
  54. package/dist/Many2many.js +80 -0
  55. package/dist/Many2many.js.map +1 -0
  56. package/dist/Many2one.d.ts +20 -0
  57. package/dist/Many2one.js +63 -0
  58. package/dist/Many2one.js.map +1 -0
  59. package/dist/NewLine.d.ts +5 -0
  60. package/dist/NewLine.js +26 -0
  61. package/dist/NewLine.js.map +1 -0
  62. package/dist/Notebook.d.ts +8 -0
  63. package/dist/Notebook.js +41 -0
  64. package/dist/Notebook.js.map +1 -0
  65. package/dist/One2many.d.ts +44 -0
  66. package/dist/One2many.js +130 -0
  67. package/dist/One2many.js.map +1 -0
  68. package/dist/Page.d.ts +5 -0
  69. package/dist/Page.js +23 -0
  70. package/dist/Page.js.map +1 -0
  71. package/dist/ProgressBar.d.ts +7 -0
  72. package/dist/ProgressBar.js +26 -0
  73. package/dist/ProgressBar.js.map +1 -0
  74. package/dist/Reference.d.ts +7 -0
  75. package/dist/Reference.js +26 -0
  76. package/dist/Reference.js.map +1 -0
  77. package/dist/SearchFilter.d.ts +28 -0
  78. package/dist/SearchFilter.js +81 -0
  79. package/dist/SearchFilter.js.map +1 -0
  80. package/dist/Selection.d.ts +20 -0
  81. package/dist/Selection.js +63 -0
  82. package/dist/Selection.js.map +1 -0
  83. package/dist/Separator.d.ts +12 -0
  84. package/dist/Separator.js +46 -0
  85. package/dist/Separator.js.map +1 -0
  86. package/dist/Text.d.ts +29 -0
  87. package/dist/Text.js +96 -0
  88. package/dist/Text.js.map +1 -0
  89. package/dist/Timeline.d.ts +17 -0
  90. package/dist/Timeline.js +56 -0
  91. package/dist/Timeline.js.map +1 -0
  92. package/dist/Tree.d.ts +31 -0
  93. package/dist/Tree.js +96 -0
  94. package/dist/Tree.js.map +1 -0
  95. package/dist/Widget.d.ts +47 -0
  96. package/dist/Widget.js +116 -0
  97. package/dist/Widget.js.map +1 -0
  98. package/dist/WidgetFactory.d.ts +9 -0
  99. package/dist/WidgetFactory.js +157 -0
  100. package/dist/WidgetFactory.js.map +1 -0
  101. package/dist/helpers/attributeParser.d.ts +6 -0
  102. package/dist/helpers/attributeParser.js +83 -0
  103. package/dist/helpers/attributeParser.js.map +1 -0
  104. package/dist/helpers/contextParser.d.ts +5 -0
  105. package/dist/helpers/contextParser.js +52 -0
  106. package/dist/helpers/contextParser.js.map +1 -0
  107. package/dist/helpers/domainParser.d.ts +5 -0
  108. package/dist/helpers/domainParser.js +26 -0
  109. package/dist/helpers/domainParser.js.map +1 -0
  110. package/dist/helpers/fieldParser.d.ts +5 -0
  111. package/dist/helpers/fieldParser.js +20 -0
  112. package/dist/helpers/fieldParser.js.map +1 -0
  113. package/dist/helpers/nodeParser.d.ts +7 -0
  114. package/dist/helpers/nodeParser.js +50 -0
  115. package/dist/helpers/nodeParser.js.map +1 -0
  116. package/dist/helpers/onChangeParser.d.ts +5 -0
  117. package/dist/helpers/onChangeParser.js +16 -0
  118. package/dist/helpers/onChangeParser.js.map +1 -0
  119. package/dist/helpers/stateParser.d.ts +14 -0
  120. package/dist/helpers/stateParser.js +46 -0
  121. package/dist/helpers/stateParser.js.map +1 -0
  122. package/dist/index.d.ts +33 -0
  123. package/dist/index.js +34 -0
  124. package/dist/index.js.map +1 -0
  125. package/package.json +49 -0
  126. package/src/Binary.ts +29 -0
  127. package/src/Boolean.ts +12 -0
  128. package/src/Button.ts +78 -0
  129. package/src/Char.ts +65 -0
  130. package/src/Container.ts +171 -0
  131. package/src/ContainerWidget.ts +105 -0
  132. package/src/Date.ts +12 -0
  133. package/src/DateTime.ts +12 -0
  134. package/src/FiberGrid.ts +9 -0
  135. package/src/Field.ts +147 -0
  136. package/src/Float.ts +45 -0
  137. package/src/FloatTime.ts +8 -0
  138. package/src/Form.ts +212 -0
  139. package/src/Group.ts +9 -0
  140. package/src/Image.ts +8 -0
  141. package/src/Integer.ts +13 -0
  142. package/src/Label.ts +46 -0
  143. package/src/Many2many.ts +59 -0
  144. package/src/Many2one.ts +44 -0
  145. package/src/NewLine.ts +9 -0
  146. package/src/Notebook.ts +24 -0
  147. package/src/One2many.ts +106 -0
  148. package/src/Page.ts +9 -0
  149. package/src/ProgressBar.ts +8 -0
  150. package/src/Reference.ts +10 -0
  151. package/src/SearchFilter.ts +81 -0
  152. package/src/Selection.ts +44 -0
  153. package/src/Separator.ts +30 -0
  154. package/src/Text.ts +76 -0
  155. package/src/Timeline.ts +44 -0
  156. package/src/Tree.ts +93 -0
  157. package/src/Widget.ts +121 -0
  158. package/src/WidgetFactory.ts +158 -0
  159. package/src/helpers/attributeParser.ts +108 -0
  160. package/src/helpers/contextParser.ts +66 -0
  161. package/src/helpers/domainParser.ts +39 -0
  162. package/src/helpers/fieldParser.ts +27 -0
  163. package/src/helpers/nodeParser.ts +57 -0
  164. package/src/helpers/onChangeParser.ts +18 -0
  165. package/src/helpers/stateParser.ts +62 -0
  166. package/src/index.ts +67 -0
  167. package/src/spec/Boolean.spec.ts +36 -0
  168. package/src/spec/Button.spec.ts +58 -0
  169. package/src/spec/Char.spec.ts +80 -0
  170. package/src/spec/Container.spec.ts +47 -0
  171. package/src/spec/ContainerWidget.spec.ts +35 -0
  172. package/src/spec/Date.spec.ts +36 -0
  173. package/src/spec/DateTime.spec.ts +36 -0
  174. package/src/spec/Float.spec.ts +29 -0
  175. package/src/spec/Form.spec.ts +976 -0
  176. package/src/spec/Group.spec.ts +32 -0
  177. package/src/spec/Label.spec.ts +46 -0
  178. package/src/spec/Many2many.spec.ts +36 -0
  179. package/src/spec/Many2one.spec.ts +36 -0
  180. package/src/spec/One2many.spec.ts +354 -0
  181. package/src/spec/SearchFilter.spec.ts +955 -0
  182. package/src/spec/Selection.spec.ts +52 -0
  183. package/src/spec/Separator.spec.ts +14 -0
  184. package/src/spec/Tree.spec.ts +214 -0
  185. package/src/spec/Widget.spec.ts +45 -0
  186. package/src/spec/WidgetFactory.spec.ts +40 -0
  187. package/src/spec/attributeParser.spec.ts +173 -0
  188. package/src/spec/contextParser.spec.ts +57 -0
  189. package/src/spec/domainParser.spec.ts +40 -0
  190. package/src/spec/fixtures/WidgetImpl.ts +10 -0
  191. package/src/spec/stateParser.spec.ts +161 -0
@@ -0,0 +1,976 @@
1
+ import Form from "../Form";
2
+ import Group from "../Group";
3
+ import Notebook from "../Notebook";
4
+ import Page from "../Page";
5
+ import Char from "../Char";
6
+ import Label from "../Label";
7
+ import Field from "../Field";
8
+ import Reference from "../Reference";
9
+ import Button from "../Button";
10
+
11
+ const XML_VIEW_FORM = `<?xml version="1.0"?>
12
+ <form string="Partner Address">
13
+ <notebook>
14
+ <page string="General">
15
+ <field colspan="4" name="partner_id" select="1"/>
16
+ <newline/>
17
+ <field name="name" select="1" required="True"/>
18
+ <field domain="[('domain', '=', 'contact')]" name="title"/>
19
+ <field name="function"/>
20
+ <field name="type" select="2"/>
21
+ <separator string="Street" colspan="4"/>
22
+ <field name="street" select="2" colspan="4" width="200"/>
23
+ <group colspan="2" col="4">
24
+ <field name="tv" select="2"/>
25
+ <newline/>
26
+ <field name="nv" colspan="4"/>
27
+ </group>
28
+ <group colspan="2" col="8">
29
+ <field name="pnp"/>
30
+ <field name="es"/>
31
+ <field name="pt"/>
32
+ <field name="pu"/>
33
+ <field name="bq" colspan="1"/>
34
+ <field name="aclarador" colspan="5"/>
35
+ </group>
36
+ <field name="street2"/>
37
+ <newline/>
38
+ <field name="zip" select="2"/>
39
+ <field name="apartat_correus"/>
40
+ <newline/>
41
+ <newline/>
42
+ <field name="id_municipi" on_change="onchange_municipi_id(id_municipi,context)"/>
43
+ <field name="id_poblacio" domain="[('municipi_id','=',id_municipi)]"/>
44
+ <field name="state_id" select="2"/>
45
+ <field completion="1" name="country_id" select="1"/>
46
+ <newline/>
47
+ <separator string="Catastre" colspan="4"/>
48
+ <field name="ref_catastral"/>
49
+ <separator string="Comunication channels" colspan="4"/>
50
+ <label string="Phone : " align="1.0"/>
51
+ <group colspan="1" col="5">
52
+ <field name="phone" nolabel="1" colspan="4"/>
53
+ <button name="action_dial_phone" string="Dial" type="object"/>
54
+ </group>
55
+ <field name="fax"/>
56
+ <newline/>
57
+ <label string="Mobile : " align="1.0"/>
58
+ <group colspan="1" col="5">
59
+ <field name="mobile" nolabel="1" colspan="4"/>
60
+ <button name="action_dial_mobile" string="Dial" type="object"/>
61
+ </group>
62
+ <field name="email" select="2" widget="email"/>
63
+ </page>
64
+ <page string="Notes">
65
+ <field name="notes" nolabel="1" colspan="4"/>
66
+ </page>
67
+ </notebook>
68
+ </form>
69
+ `;
70
+
71
+ const FIELDS = {
72
+ aclarador: {
73
+ size: 256,
74
+ string: "Aclarador",
75
+ type: "char",
76
+ views: {},
77
+ },
78
+ apartat_correus: {
79
+ size: 5,
80
+ string: "Apartat de Correus",
81
+ type: "char",
82
+ views: {},
83
+ },
84
+ bq: { size: 4, string: "Bloc", type: "char", views: {} },
85
+ country_id: {
86
+ context: "",
87
+ domain: [],
88
+ relation: "res.country",
89
+ size: 64,
90
+ string: "Country",
91
+ type: "many2one",
92
+ views: {},
93
+ },
94
+ email: { size: 240, string: "E-Mail", type: "char", views: {} },
95
+ es: { size: 10, string: "Escala", type: "char", views: {} },
96
+ fax: { size: 64, string: "Fax", type: "char", views: {} },
97
+ function: {
98
+ context: "",
99
+ domain: [],
100
+ relation: "res.partner.function",
101
+ size: 64,
102
+ string: "Function",
103
+ type: "many2one",
104
+ views: {},
105
+ },
106
+ id_municipi: {
107
+ context: "",
108
+ domain: [],
109
+ relation: "res.municipi",
110
+ size: 64,
111
+ string: "Municipi",
112
+ type: "many2one",
113
+ views: {},
114
+ },
115
+ id_poblacio: {
116
+ context: "",
117
+ domain: [],
118
+ relation: "res.poblacio",
119
+ size: 64,
120
+ string: "Població",
121
+ type: "many2one",
122
+ views: {},
123
+ },
124
+ mobile: { size: 64, string: "Mobile", type: "char", views: {} },
125
+ name: { size: 128, string: "Contact Name", type: "char", views: {} },
126
+ notes: { string: "Notes", type: "text", views: {} },
127
+ nv: { size: 256, string: "Carrer", type: "char", views: {} },
128
+ partner_id: {
129
+ context: "",
130
+ domain: [],
131
+ help: "Keep empty for a private address, not related to partner.",
132
+ relation: "res.partner",
133
+ select: true,
134
+ size: 64,
135
+ string: "Partner",
136
+ type: "many2one",
137
+ views: {},
138
+ },
139
+ phone: { size: 64, string: "Phone", type: "char", views: {} },
140
+ pnp: { size: 10, string: "Número", type: "char", views: {} },
141
+ pt: { size: 10, string: "Planta", type: "char", views: {} },
142
+ pu: { size: 10, string: "Porta", type: "char", views: {} },
143
+ ref_catastral: {
144
+ size: 20,
145
+ string: "Ref Catastral (c)",
146
+ type: "char",
147
+ views: {},
148
+ },
149
+ state_id: {
150
+ context: "",
151
+ domain: "[('country_id','=',country_id)]",
152
+ relation: "res.country.state",
153
+ size: 64,
154
+ string: "Fed. State",
155
+ type: "many2one",
156
+ views: {},
157
+ },
158
+ street: {
159
+ digits: [16, 2],
160
+ readonly: 1,
161
+ size: 128,
162
+ string: "Street",
163
+ type: "char",
164
+ views: {},
165
+ },
166
+ street2: { size: 128, string: "Street2", type: "char", views: {} },
167
+ title: {
168
+ selection: [
169
+ ["Ms.", "Madam"],
170
+ ["Mss", "Miss"],
171
+ ["M.", "Sir"],
172
+ ["", ""],
173
+ ],
174
+ size: 32,
175
+ string: "Title",
176
+ type: "selection",
177
+ views: {},
178
+ },
179
+ tv: {
180
+ context: "",
181
+ domain: [],
182
+ relation: "res.tipovia",
183
+ size: 64,
184
+ string: "Tipus Via",
185
+ type: "many2one",
186
+ views: {},
187
+ },
188
+ type: {
189
+ help:
190
+ "Used to select automatically the right address according to the context in sales and purchases documents.",
191
+ selection: [
192
+ ["default", "Default"],
193
+ ["invoice", "Invoice"],
194
+ ["delivery", "Delivery"],
195
+ ["contact", "Contact"],
196
+ ["other", "Other"],
197
+ ["ov", "Oficina Virtual"],
198
+ ],
199
+ string: "Address Type",
200
+ type: "selection",
201
+ views: {},
202
+ },
203
+ zip: {
204
+ change_default: true,
205
+ size: 24,
206
+ string: "Zip",
207
+ type: "char",
208
+ views: {},
209
+ },
210
+ };
211
+
212
+ /*
213
+ function printRow(row, tab) {
214
+ console.log("-".repeat(80));
215
+ row.forEach((el) => {
216
+ const container = el.container || false;
217
+ const prefix = " ".repeat(tab);
218
+ if (container) {
219
+ console.log(prefix, el.type, el.container.columns);
220
+ tab = tab + 4;
221
+ container.rows.forEach((row) => {
222
+ printRow(row, tab);
223
+ });
224
+ } else {
225
+ console.log(prefix, el);
226
+ }
227
+ });
228
+ }
229
+ */
230
+
231
+ describe("A Form", () => {
232
+ it("should parse xml", () => {
233
+ const form = new Form(FIELDS);
234
+ form.parse(XML_VIEW_FORM);
235
+ //form.container.rows.forEach(row => {
236
+ // printRow(row, 0);
237
+ //});
238
+ expect(form.fields).toBeDefined();
239
+ expect(form.container.rows.length).toBeGreaterThan(0);
240
+ });
241
+
242
+ it("should be able to fit a widget with greater colspan than page container columns", () => {
243
+ const fields = {
244
+ char1: { size: 128, string: "Name", type: "char", views: {} },
245
+ };
246
+ const xmlViewForm = `<?xml version="1.0"?>
247
+ <form string="Form1">
248
+ <notebook>
249
+ <page string="Page1" col="8">
250
+ <field colspan="16" name="char1" />
251
+ </page>
252
+ </notebook>
253
+ </form>`;
254
+ const form = new Form(fields);
255
+ form.parse(xmlViewForm);
256
+
257
+ const notebook = form.container.rows[0][0] as Notebook;
258
+ expect(notebook).toBeInstanceOf(Notebook);
259
+ const page = notebook.container.rows[0][0] as Page;
260
+ expect(page).toBeInstanceOf(Page);
261
+ const labelField = page.container.rows[0][0];
262
+ expect(labelField).toBeInstanceOf(Label);
263
+ const charField = page.container.rows[0][1];
264
+ expect(charField).toBeInstanceOf(Char);
265
+
266
+ // Should match the container's col
267
+ expect(charField.colspan + labelField.colspan).toBe(8);
268
+ });
269
+
270
+ it("should be able to find a widget by id", () => {
271
+ const fields = {
272
+ char1: { size: 128, string: "Name", type: "char", views: {} },
273
+ };
274
+ const xmlViewForm = `<?xml version="1.0"?>
275
+ <form string="Form1">
276
+ <notebook>
277
+ <page string="Page1" col="8">
278
+ <field colspan="8" name="char1" />
279
+ </page>
280
+ </notebook>
281
+ </form>`;
282
+ const form = new Form(fields);
283
+ form.parse(xmlViewForm);
284
+
285
+ expect(form.findById("char1")).toBeInstanceOf(Char);
286
+ });
287
+
288
+ it("should return undefined when a widget is not found by id", () => {
289
+ const fields = {
290
+ char1: { size: 128, string: "Name", type: "char", views: {} },
291
+ };
292
+ const xmlViewForm = `<?xml version="1.0"?>
293
+ <form string="Form1">
294
+ <notebook>
295
+ <page string="Page1" col="8">
296
+ <field colspan="8" name="char1" />
297
+ </page>
298
+ </notebook>
299
+ </form>`;
300
+ const form = new Form(fields);
301
+ form.parse(xmlViewForm);
302
+
303
+ expect(form.findById("non_existent_widget")).toBeNull();
304
+ });
305
+
306
+ it("should be able to find the first widget with matching id", () => {
307
+ const fields = {
308
+ char1: { size: 128, string: "Name", type: "char", views: {} },
309
+ };
310
+ const xmlViewForm = `<?xml version="1.0"?>
311
+ <form string="Form1">
312
+ <notebook>
313
+ <page string="Page1" col="8">
314
+ <field colspan="1" name="char1" />
315
+ <field colspan="2" name="char1" />
316
+ </page>
317
+ </notebook>
318
+ </form>`;
319
+ const form = new Form(fields);
320
+ form.parse(xmlViewForm);
321
+
322
+ const field = form.findById("char1");
323
+ expect(field).not.toBeNull();
324
+ if (field) {
325
+ expect(field).toBeInstanceOf(Char);
326
+ expect(field.colspan).toBe(1);
327
+ }
328
+ });
329
+
330
+ it("should be able to find a group widget with matching id", () => {
331
+ const fields = {
332
+ char1: { size: 128, string: "Name", type: "char", views: {} },
333
+ };
334
+ const xmlViewForm = `<?xml version="1.0"?>
335
+ <form string="Form1">
336
+ <group name="group">
337
+ <field colspan="1" name="char1" />
338
+ <field colspan="2" name="char1" />
339
+ </group>
340
+ </form>`;
341
+ const form = new Form(fields);
342
+ form.parse(xmlViewForm);
343
+
344
+ const field = form.findById("group");
345
+ expect(field).not.toBeNull();
346
+ if (field) {
347
+ expect(field).toBeInstanceOf(Group);
348
+ }
349
+ });
350
+
351
+ it("should be able to parse a field with tooltip", () => {
352
+ const fields = {
353
+ char1: {
354
+ size: 128,
355
+ string: "Name",
356
+ type: "char",
357
+ help: "tooltip string",
358
+ },
359
+ };
360
+ const xmlViewForm = `<?xml version="1.0"?>
361
+ <form string="Form1">
362
+ <group name="group">
363
+ <field colspan="1" name="char1" />
364
+ </group>
365
+ </form>`;
366
+ const form = new Form(fields);
367
+ form.parse(xmlViewForm);
368
+
369
+ const field = form.findById("char1") as Char;
370
+ expect(field).not.toBeNull();
371
+ expect(field.tooltip).toBe("tooltip string");
372
+ });
373
+
374
+ it("should properly parse a password field", () => {
375
+ const arch =
376
+ '<group><field name="password" password="True" readonly="0"/></group>';
377
+ const fields = {
378
+ password: {
379
+ help:
380
+ "Keep empty if you don't want the user to be able to connect on the system.",
381
+ invisible: true,
382
+ size: 64,
383
+ string: "Password",
384
+ type: "char",
385
+ views: {},
386
+ },
387
+ };
388
+ const form = new Form(fields);
389
+ form.parse(arch);
390
+
391
+ const field = form.findById("password") as Char;
392
+ expect(field.isPassword).toBeTruthy();
393
+ });
394
+
395
+ it("should properly parse a normal char field without password flag", () => {
396
+ const arch = '<group><field name="password" readonly="0"/></group>';
397
+ const fields = {
398
+ password: {
399
+ help:
400
+ "Keep empty if you don't want the user to be able to connect on the system.",
401
+ invisible: true,
402
+ size: 64,
403
+ string: "Password",
404
+ type: "char",
405
+ views: {},
406
+ },
407
+ };
408
+ const form = new Form(fields);
409
+ form.parse(arch);
410
+
411
+ const field = form.findById("password") as Char;
412
+ expect(field.isPassword).toBeFalsy();
413
+ });
414
+
415
+ it("should properly parse a newline and reflect proper rows for it", () => {
416
+ const arch =
417
+ '<form><field name="field"/><newline /><field name="field" readonly="0"/></form>';
418
+ const fields = {
419
+ field: {
420
+ type: "char",
421
+ },
422
+ };
423
+ const form = new Form(fields);
424
+ form.parse(arch);
425
+ expect(form.container.rows.length).toBe(2);
426
+ });
427
+
428
+ it("should properly parse a field with invisible parameter", () => {
429
+ const arch = '<form><field name="field" invisible="1"/></form>';
430
+ const fields = {
431
+ field: {
432
+ type: "char",
433
+ },
434
+ };
435
+ const form = new Form(fields);
436
+ form.parse(arch);
437
+ const widget = form.findById("field");
438
+ expect(widget).toBeTruthy();
439
+ expect(widget!.invisible).toBeTruthy();
440
+ });
441
+
442
+ it("should properly parse invisible parameter to false by default", () => {
443
+ const arch = '<form><field name="field"/></form>';
444
+ const fields = {
445
+ field: {
446
+ type: "char",
447
+ },
448
+ };
449
+ const form = new Form(fields);
450
+ form.parse(arch);
451
+ const widget = form.findById("field");
452
+ expect(widget).toBeTruthy();
453
+ expect(widget!.invisible).toBeFalsy();
454
+ });
455
+
456
+ it("should add a widget that doesn't fit in a new line with label", () => {
457
+ const arch =
458
+ '<form><field name="field" colspan="6" /><field name="newlinefield" colspan="4" /></form>';
459
+ const fields = {
460
+ field: {
461
+ type: "char",
462
+ },
463
+ newlinefield: {
464
+ type: "char",
465
+ },
466
+ };
467
+ const form = new Form(fields);
468
+ form.parse(arch);
469
+ expect(form.container.rows.length).toBe(2);
470
+ expect(form.container.rows[0].length).toBe(2);
471
+ expect(form.container.rows[1].length).toBe(2);
472
+ });
473
+
474
+ it("should add a label with tooltip for a field with tooltip", () => {
475
+ const arch = '<form><field name="field" help="Tooltip" /></form>';
476
+ const fields = {
477
+ field: {
478
+ type: "char",
479
+ },
480
+ };
481
+ const form = new Form(fields);
482
+ form.parse(arch);
483
+ expect(form.container.rows[0].length).toBe(2);
484
+ const row = form.container.rows[0];
485
+ const label = row[0] as Label;
486
+ const char = row[1] as Char;
487
+ expect(label.tooltip).toBe(char.tooltip);
488
+ });
489
+
490
+ it("Must throw an error if a field isn't present in field definitions", () => {
491
+ const parseInvalidForm = () => {
492
+ const arch = '<form><field name="example" help="Tooltip" /></form>';
493
+ const fields = {};
494
+ const form = new Form(fields);
495
+ form.parse(arch);
496
+ };
497
+
498
+ expect(parseInvalidForm).toThrow(
499
+ "Field example doesn't exist in fields defintion"
500
+ );
501
+ });
502
+
503
+ it("Should parse form string title properly", () => {
504
+ const fields = {
505
+ char1: { size: 128, string: "Name", type: "char", views: {} },
506
+ };
507
+ const xmlViewForm = `<?xml version="1.0"?>
508
+ <form string="Form1">
509
+ <notebook>
510
+ <page string="Page1" col="8">
511
+ <field colspan="8" name="char1" />
512
+ </page>
513
+ </notebook>
514
+ </form>`;
515
+ const form = new Form(fields);
516
+ form.parse(xmlViewForm);
517
+
518
+ const formTitle = form.string;
519
+ expect(formTitle).toBe("Form1");
520
+ });
521
+
522
+ it("Should parse form string title as null if we don't pass it", () => {
523
+ const arch =
524
+ '<form><field name="field"/><newline /><field name="field" readonly="0"/></form>';
525
+ const fields = {
526
+ field: {
527
+ type: "char",
528
+ },
529
+ };
530
+ const form = new Form(fields);
531
+ form.parse(arch);
532
+ const formTitle = form.string;
533
+ expect(formTitle).toBeNull();
534
+ });
535
+
536
+ it("Should parse a readonly group with its children set to readonly too", () => {
537
+ const arch =
538
+ '<form><group readonly="1"><field name="field1"/><newline /><field name="field2" readonly="0"/></group></form>';
539
+ const fields = {
540
+ field1: {
541
+ type: "char",
542
+ },
543
+ field2: {
544
+ type: "char",
545
+ },
546
+ };
547
+ const form = new Form(fields);
548
+ form.parse(arch);
549
+ const field1 = form.findById("field1")!;
550
+ expect(field1.readOnly).toBeTruthy();
551
+ const field2 = form.findById("field2")!;
552
+ expect(field2.readOnly).toBeTruthy();
553
+ });
554
+
555
+ it("Should parse a readonly form with its children set to readonly too", () => {
556
+ const arch =
557
+ '<form><group><field name="field1"/><newline /><field name="field2" readonly="0"/></group></form>';
558
+ const fields = {
559
+ field1: {
560
+ type: "char",
561
+ },
562
+ field2: {
563
+ type: "char",
564
+ },
565
+ };
566
+ const form = new Form(fields);
567
+ form.parse(arch, { readOnly: true });
568
+ const field1 = form.findById("field1")!;
569
+ expect(field1.readOnly).toBeTruthy();
570
+ const field2 = form.findById("field2")!;
571
+ expect(field2.readOnly).toBeTruthy();
572
+ });
573
+
574
+ it("Should be able to retrieve type from From instance", () => {
575
+ const arch =
576
+ '<form><group><field name="field1"/><newline /><field name="field2" readonly="0"/></group></form>';
577
+ const fields = {
578
+ field1: {
579
+ type: "char",
580
+ },
581
+ field2: {
582
+ type: "char",
583
+ },
584
+ };
585
+ const form = new Form(fields);
586
+ form.parse(arch);
587
+ expect(form.type).toBe("form");
588
+ });
589
+
590
+ it("Should be able to parse attributes", () => {
591
+ const arch =
592
+ "<form><group><button name=\"field1\" attrs=\"{'invisible':[('per_enviar', '=', 'postal')]}\"/><newline /><field name=\"field2\" readonly=\"0\"/></group></form>";
593
+ const fields = {
594
+ field1: {
595
+ type: "button",
596
+ },
597
+ field2: {
598
+ type: "char",
599
+ },
600
+ per_enviar: {
601
+ type: "char",
602
+ },
603
+ };
604
+ const form = new Form(fields);
605
+ form.parse(arch, { values: { per_enviar: "postal" } });
606
+ expect(form.type).toBe("form");
607
+ const field1 = form.findById("field1")!;
608
+ expect(field1.invisible).toBeTruthy();
609
+ });
610
+
611
+ it("Should be able to parse states - unmet condition with previous value", () => {
612
+ const arch = '<form><group><field name="data_final" /></group></form>';
613
+ const values = { state: "random_state" };
614
+ const fields = {
615
+ data_final: {
616
+ readonly: true,
617
+ states: {
618
+ draft: [["readonly", false]],
619
+ },
620
+ string: "Data final",
621
+ type: "date",
622
+ views: {},
623
+ },
624
+ };
625
+ const form = new Form(fields);
626
+ form.parse(arch, { values });
627
+ expect(form.type).toBe("form");
628
+ const field1 = form.findById("data_final")!;
629
+ expect(field1.readOnly).toBeTruthy();
630
+ });
631
+
632
+ it("Should be able to parse states - matched condition", () => {
633
+ const arch = '<form><group><field name="data_final" /></group></form>';
634
+ const values = { state: "draft" };
635
+ const fields = {
636
+ data_final: {
637
+ readonly: true,
638
+ states: {
639
+ draft: [["readonly", false]],
640
+ },
641
+ string: "Data final",
642
+ type: "date",
643
+ views: {},
644
+ },
645
+ };
646
+ const form = new Form(fields);
647
+ form.parse(arch, { values });
648
+ expect(form.type).toBe("form");
649
+ const field1 = form.findById("data_final")!;
650
+ expect(field1.readOnly).toBeFalsy();
651
+ });
652
+
653
+ it("Should be able to parse a button states - unmatched condition => invisible button", () => {
654
+ const arch =
655
+ '<form><group><button name="button" states="draft,pending,complete" /></group></form>';
656
+ const values = { state: "draft" };
657
+ const fields = {
658
+ button: {
659
+ type: "button",
660
+ },
661
+ };
662
+ const form = new Form(fields);
663
+ form.parse(arch, { values });
664
+ expect(form.type).toBe("form");
665
+ const field1 = form.findById("button")!;
666
+ expect(field1.invisible).toBeFalsy();
667
+ });
668
+
669
+ it("Should be able to parse a button states - matched condition => visible button", () => {
670
+ const arch =
671
+ '<form><group><button name="button" states="draft,pending,complete" /></group></form>';
672
+ const values = { state: "other_state" };
673
+ const fields = {
674
+ button: {
675
+ type: "button",
676
+ },
677
+ };
678
+ const form = new Form(fields);
679
+ form.parse(arch, { values });
680
+ expect(form.type).toBe("form");
681
+ const field1 = form.findById("button")!;
682
+ expect(field1.invisible).toBeTruthy();
683
+ });
684
+
685
+ it("should be able to parse a field with inline label string attribute", () => {
686
+ const fields = {
687
+ char1: {
688
+ size: 128,
689
+ string: "Name",
690
+ type: "char",
691
+ help: "tooltip string",
692
+ },
693
+ };
694
+ const xmlViewForm = `<?xml version="1.0"?>
695
+ <form string="Form1">
696
+ <group name="group">
697
+ <field colspan="1" name="char1" string="Label override" />
698
+ </group>
699
+ </form>`;
700
+ const form = new Form(fields);
701
+ form.parse(xmlViewForm);
702
+
703
+ const field = form.findById("char1") as Char;
704
+ expect(field.label).toBe("Label override");
705
+ });
706
+
707
+ it("should be able to parse a Reference widget", () => {
708
+ const fields = {
709
+ ref: {
710
+ selection: [
711
+ ["product.product", "Product"],
712
+ ["purchase.order", "Purchase Order"],
713
+ ["account.invoice", "Invoice"],
714
+ ["stock.production.lot", "Production Lot"],
715
+ ["giscedata.polissa", "Pòlissa"],
716
+ ["giscegas.polissa", "Contrato"],
717
+ ["giscedata.signatura.process", "Firma"],
718
+ ["giscedata.crm.lead", "Oferta/Oportunidad"],
719
+ ["crm.case", "Case"],
720
+ ["giscedata.switching", "Caso ATR"],
721
+ ],
722
+ size: 128,
723
+ string: "Reference",
724
+ type: "reference",
725
+ views: {},
726
+ },
727
+ };
728
+ const xmlViewForm = `<?xml version="1.0"?>
729
+ <form string="Form1">
730
+ <group name="group">
731
+ <field colspan="1" name="ref" />
732
+ </group>
733
+ </form>`;
734
+ const form = new Form(fields);
735
+ form.parse(xmlViewForm);
736
+
737
+ const field = form.findById("ref") as Reference;
738
+ expect(field.selectionValues.size).toBe(10);
739
+ });
740
+
741
+ it("should be able to parse a Button with the specific action attributes", () => {
742
+ const fields = {
743
+ button: {
744
+ type: "button",
745
+ },
746
+ };
747
+ const xmlViewForm = `<?xml version="1.0"?>
748
+ <form string="Form1">
749
+ <button name="button" string="Generar periodes" type="object" confirm="Text modal" />
750
+ </form>`;
751
+ const form = new Form(fields);
752
+ form.parse(xmlViewForm);
753
+
754
+ const field = form.findById("button") as Button;
755
+ expect(field.confirmMessage).toBe("Text modal");
756
+ expect(field.buttonType).toBe("object");
757
+ });
758
+
759
+ it("should be able to parse a Button by default to type workflow", () => {
760
+ const fields = {
761
+ button: {
762
+ type: "button",
763
+ },
764
+ };
765
+ const xmlViewForm = `<?xml version="1.0"?>
766
+ <form string="Form1">
767
+ <button name="button" string="Generar periodes" confirm="Text modal" />
768
+ </form>`;
769
+ const form = new Form(fields);
770
+ form.parse(xmlViewForm);
771
+
772
+ const field = form.findById("button") as Button;
773
+ expect(field.confirmMessage).toBe("Text modal");
774
+ expect(field.buttonType).toBe("workflow");
775
+ });
776
+
777
+ it("should be able to parse a Button with special cancel case", () => {
778
+ const fields = {
779
+ button: {
780
+ type: "button",
781
+ },
782
+ };
783
+ const xmlViewForm = `<?xml version="1.0"?>
784
+ <form string="Form1">
785
+ <button name="button" string="Generar periodes" special="cancel" />
786
+ </form>`;
787
+ const form = new Form(fields);
788
+ form.parse(xmlViewForm);
789
+
790
+ const field = form.findById("button") as Button;
791
+ expect(field.buttonType).toBe("cancel");
792
+ });
793
+
794
+ it("should be able to parse a Button with context", () => {
795
+ const fields = {
796
+ button: {
797
+ type: "button",
798
+ },
799
+ potencia: {
800
+ type: "float",
801
+ },
802
+ };
803
+ const xmlViewForm = `<?xml version="1.0"?>
804
+ <form string="Form1">
805
+ <button name="button" string="Generar periodes" special="cancel" context="{'power': potencia, 'tarifa_id': tarifa, 'tensio_id': tensio_normalitzada, 'model': 'giscedata.polissa', 'field': 'potencia'}"/>
806
+ </form>`;
807
+ const form = new Form(fields);
808
+ form.parse(xmlViewForm, {
809
+ values: {
810
+ potencia: 45,
811
+ },
812
+ });
813
+
814
+ const field = form.findById("button") as Button;
815
+ expect(field.context).toBeDefined();
816
+ expect(field.context!["power"]).toBe(45);
817
+ });
818
+
819
+ it("should be able to parse a Button with context with a many2one value", () => {
820
+ const fields = {
821
+ button: {
822
+ type: "button",
823
+ },
824
+ tarifa: {
825
+ type: "many2one",
826
+ },
827
+ };
828
+ const xmlViewForm = `<?xml version="1.0"?>
829
+ <form string="Form1">
830
+ <button name="button" string="Generar periodes" special="cancel" context="{'power': potencia, 'tarifa_id': tarifa, 'tensio_id': tensio_normalitzada, 'model': 'giscedata.polissa', 'field': 'potencia'}"/>
831
+ </form>`;
832
+ const form = new Form(fields);
833
+ form.parse(xmlViewForm, {
834
+ values: {
835
+ potencia: 45,
836
+ tarifa: [1, "2.0A"],
837
+ },
838
+ });
839
+
840
+ const field = form.findById("button") as Button;
841
+ expect(field.context).toBeDefined();
842
+ expect(field.context!["tarifa_id"]).toBe(1);
843
+ });
844
+
845
+ it("should be able to parse a Form with fields contexts", () => {
846
+ const fields = {
847
+ field_char: {
848
+ type: "char",
849
+ },
850
+ field_id: {
851
+ type: "integer",
852
+ },
853
+ field_num: {
854
+ type: "integer",
855
+ },
856
+ button: {
857
+ type: "button",
858
+ },
859
+ tarifa: {
860
+ type: "many2one",
861
+ },
862
+ potencia: {
863
+ type: "float",
864
+ },
865
+ };
866
+ const xmlViewForm = `<?xml version="1.0"?>
867
+ <form string="Form1">
868
+ <field name="field_id" colspan="4" nolabel="1" context="{'cups_id': active_id}"/>
869
+ <field name="field_char" colspan="4" nolabel="1" context="{'test_string': 'test'}"/>
870
+ <button name="button" string="Generar periodes" special="cancel" context="{'power': potencia, 'tarifa_id': tarifa, 'tensio_id': tensio_normalitzada, 'model': 'giscedata.polissa', 'field': 'potencia'}"/>
871
+ </form>`;
872
+ const form = new Form(fields);
873
+ form.parse(xmlViewForm, {
874
+ values: {
875
+ id: 99,
876
+ potencia: 45,
877
+ tarifa: [1, "2.0A"],
878
+ },
879
+ });
880
+ expect(form.context).toBeDefined();
881
+ expect(form.context.active_id).toBe(99);
882
+ expect(Array.isArray(form.context.active_ids)).toBeTruthy();
883
+ expect(form.context.active_ids[0]).toBe(99);
884
+ expect(form.context.cups_id).toBe(99);
885
+ expect(form.context.test_string).toBe("test");
886
+ expect(form.context.power).toBeUndefined();
887
+
888
+ const field = form.findById("button") as Button;
889
+ expect(field.context).toBeDefined();
890
+ expect(field.context!["tarifa_id"]).toBe(1);
891
+ expect(field.context!["power"]).toBe(45);
892
+ });
893
+
894
+ it("should be able to parse a on_change field", () => {
895
+ const fields = {
896
+ field_char: {
897
+ type: "char",
898
+ },
899
+ field_id: {
900
+ type: "integer",
901
+ },
902
+ field_num: {
903
+ type: "integer",
904
+ },
905
+ button: {
906
+ type: "button",
907
+ },
908
+ tarifa: {
909
+ type: "many2one",
910
+ },
911
+ potencia: {
912
+ type: "float",
913
+ },
914
+ };
915
+ const xmlViewForm = `<?xml version="1.0"?>
916
+ <form string="Form1">
917
+ <field name="field_id" colspan="4" nolabel="1" />
918
+ <field name="field_char" on_change="on_change_partner_address_id(partner_address_id, context)" colspan="4" nolabel="1" />
919
+ </form>`;
920
+ const form = new Form(fields);
921
+ form.parse(xmlViewForm, {
922
+ values: {
923
+ partner_address_id: 29,
924
+ },
925
+ });
926
+
927
+ const field = form.findById("field_char") as Field;
928
+ expect(field).toBeDefined();
929
+
930
+ expect(form.onChangeFields).toBeDefined();
931
+ expect(form.onChangeFields!["field_char"].method).toBe(
932
+ "on_change_partner_address_id"
933
+ );
934
+ expect(form.onChangeFields!["field_char"].args).toBeDefined();
935
+ expect(form.onChangeFields!["field_char"].args[0]).toBe(
936
+ "partner_address_id"
937
+ );
938
+ expect(form.onChangeFields!["field_char"].args[1]).toBe("context");
939
+ });
940
+
941
+ it("should be able to parse domain for the whole form", () => {
942
+ const fields = {
943
+ field_char: {
944
+ type: "char",
945
+ },
946
+ field_id: {
947
+ type: "integer",
948
+ domain: "[('field_id', '=', active_id)]",
949
+ },
950
+ tarifa: {
951
+ type: "many2one",
952
+ },
953
+ };
954
+ const xmlViewForm = `<?xml version="1.0"?>
955
+ <form string="Form1" domain="[('x', '=', 'y')]">
956
+ <field name="field_id" colspan="4" nolabel="1" />
957
+ <field name="field_char" colspan="4" nolabel="1" domain="[('bar', '=', tarifa)]"/>
958
+ <field name="tarifa" colspan="4" nolabel="1" />
959
+ </form>`;
960
+ const form = new Form(fields);
961
+ form.parse(xmlViewForm, {
962
+ values: {
963
+ field_char: "test",
964
+ field_id: 45,
965
+ tarifa: [1, "2.0A"],
966
+ active_id: 43,
967
+ },
968
+ });
969
+
970
+ const field_id = form.findById("field_id");
971
+ const field_char = form.findById("field_char");
972
+
973
+ expect(field_id!.domain!).toBe("[('field_id', '=', active_id)]");
974
+ expect(field_char!.domain!).toBe("[('bar', '=', tarifa)]");
975
+ });
976
+ });