zinkee 0.1.7 → 0.1.9
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/README.md +43 -17
- package/dist/index.js +205 -344
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/schemas.test.ts +229 -6
- package/src/commands/schemas.ts +31 -0
- package/src/index.test.ts +11 -5
- package/src/utils/examples.test.ts +44 -8
- package/src/utils/examples.ts +447 -66
- package/src/utils/schema-fields.ts +571 -86
- package/.claude/settings.local.json +0 -14
package/package.json
CHANGED
|
@@ -269,6 +269,95 @@ describe("schemas commands", () => {
|
|
|
269
269
|
});
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
+
it("creates fields with a default value flag", async () => {
|
|
273
|
+
const configPath = createApiConfig();
|
|
274
|
+
const output = createStdoutBuffer();
|
|
275
|
+
const fetchMock = createFetchMock();
|
|
276
|
+
mockSchemaLookup(fetchMock);
|
|
277
|
+
fetchMock.mockResolvedValueOnce(
|
|
278
|
+
jsonResponse({
|
|
279
|
+
id: "field-budget",
|
|
280
|
+
type: "NumberField",
|
|
281
|
+
slug: "budget",
|
|
282
|
+
label: "Budget",
|
|
283
|
+
required: true,
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
286
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
287
|
+
|
|
288
|
+
await program.parseAsync([
|
|
289
|
+
"node",
|
|
290
|
+
"zinkee",
|
|
291
|
+
"--json",
|
|
292
|
+
"schemas",
|
|
293
|
+
"fields",
|
|
294
|
+
"create",
|
|
295
|
+
"contacts",
|
|
296
|
+
"--type",
|
|
297
|
+
"number",
|
|
298
|
+
"--slug",
|
|
299
|
+
"budget",
|
|
300
|
+
"--label",
|
|
301
|
+
"Budget",
|
|
302
|
+
"--required",
|
|
303
|
+
"true",
|
|
304
|
+
"--default",
|
|
305
|
+
"0",
|
|
306
|
+
]);
|
|
307
|
+
|
|
308
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body))).toEqual({
|
|
309
|
+
type: "NumberField",
|
|
310
|
+
slug: "budget",
|
|
311
|
+
label: "Budget",
|
|
312
|
+
required: true,
|
|
313
|
+
config: {
|
|
314
|
+
defaultValue: "0",
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("lets the default value flag override raw config defaults", async () => {
|
|
320
|
+
const configPath = createApiConfig();
|
|
321
|
+
const output = createStdoutBuffer();
|
|
322
|
+
const fetchMock = createFetchMock();
|
|
323
|
+
mockSchemaLookup(fetchMock);
|
|
324
|
+
fetchMock.mockResolvedValueOnce(
|
|
325
|
+
jsonResponse({
|
|
326
|
+
id: "field-budget",
|
|
327
|
+
type: "NumberField",
|
|
328
|
+
slug: "budget",
|
|
329
|
+
label: "Budget",
|
|
330
|
+
}),
|
|
331
|
+
);
|
|
332
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
333
|
+
|
|
334
|
+
await program.parseAsync([
|
|
335
|
+
"node",
|
|
336
|
+
"zinkee",
|
|
337
|
+
"--json",
|
|
338
|
+
"schemas",
|
|
339
|
+
"fields",
|
|
340
|
+
"create",
|
|
341
|
+
"contacts",
|
|
342
|
+
"--raw",
|
|
343
|
+
'{"type":"number","slug":"budget","label":"Budget","config":{"defaultValue":"10","formatOptions":{"format":"number"}}}',
|
|
344
|
+
"--default",
|
|
345
|
+
"0",
|
|
346
|
+
]);
|
|
347
|
+
|
|
348
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body))).toEqual({
|
|
349
|
+
type: "NumberField",
|
|
350
|
+
slug: "budget",
|
|
351
|
+
label: "Budget",
|
|
352
|
+
config: {
|
|
353
|
+
defaultValue: "0",
|
|
354
|
+
formatOptions: {
|
|
355
|
+
format: "number",
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
272
361
|
it("updates fields from a raw file and normalizes field UUID selectors", async () => {
|
|
273
362
|
const configPath = createApiConfig();
|
|
274
363
|
const output = createStdoutBuffer();
|
|
@@ -342,6 +431,52 @@ describe("schemas commands", () => {
|
|
|
342
431
|
});
|
|
343
432
|
});
|
|
344
433
|
|
|
434
|
+
it("updates fields with a default value flag", async () => {
|
|
435
|
+
const configPath = createApiConfig();
|
|
436
|
+
const output = createStdoutBuffer();
|
|
437
|
+
const fetchMock = createFetchMock();
|
|
438
|
+
mockSchemaLookup(fetchMock);
|
|
439
|
+
fetchMock.mockResolvedValueOnce(
|
|
440
|
+
jsonResponse({
|
|
441
|
+
id: "field-budget",
|
|
442
|
+
type: "NumberField",
|
|
443
|
+
slug: "budget",
|
|
444
|
+
label: "Budget",
|
|
445
|
+
required: false,
|
|
446
|
+
}),
|
|
447
|
+
);
|
|
448
|
+
fetchMock.mockResolvedValueOnce(
|
|
449
|
+
jsonResponse({
|
|
450
|
+
id: "field-budget",
|
|
451
|
+
type: "NumberField",
|
|
452
|
+
slug: "budget",
|
|
453
|
+
label: "Budget",
|
|
454
|
+
required: false,
|
|
455
|
+
}),
|
|
456
|
+
);
|
|
457
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
458
|
+
|
|
459
|
+
await program.parseAsync([
|
|
460
|
+
"node",
|
|
461
|
+
"zinkee",
|
|
462
|
+
"--json",
|
|
463
|
+
"schemas",
|
|
464
|
+
"fields",
|
|
465
|
+
"update",
|
|
466
|
+
"contacts",
|
|
467
|
+
"550E8400-E29B-41D4-A716-446655440002",
|
|
468
|
+
"--default",
|
|
469
|
+
"0",
|
|
470
|
+
]);
|
|
471
|
+
|
|
472
|
+
expect(JSON.parse(String(fetchMock.mock.calls[2]?.[1]?.body))).toEqual({
|
|
473
|
+
type: "NumberField",
|
|
474
|
+
config: {
|
|
475
|
+
defaultValue: "0",
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
345
480
|
it("strips formula status from update payloads", async () => {
|
|
346
481
|
const configPath = createApiConfig();
|
|
347
482
|
const output = createStdoutBuffer();
|
|
@@ -590,6 +725,94 @@ describe("schemas commands", () => {
|
|
|
590
725
|
});
|
|
591
726
|
});
|
|
592
727
|
|
|
728
|
+
it("normalizes schedule field shorthand payloads into the backend shape", async () => {
|
|
729
|
+
const configPath = createApiConfig();
|
|
730
|
+
const output = createStdoutBuffer();
|
|
731
|
+
const fetchMock = createFetchMock();
|
|
732
|
+
mockSchemaLookup(fetchMock);
|
|
733
|
+
fetchMock.mockResolvedValueOnce(
|
|
734
|
+
jsonResponse({
|
|
735
|
+
id: "field-5",
|
|
736
|
+
type: "ScheduleField",
|
|
737
|
+
slug: "monthly-recurrence",
|
|
738
|
+
label: "Monthly Recurrence",
|
|
739
|
+
}),
|
|
740
|
+
);
|
|
741
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
742
|
+
|
|
743
|
+
await program.parseAsync([
|
|
744
|
+
"node",
|
|
745
|
+
"zinkee",
|
|
746
|
+
"--json",
|
|
747
|
+
"schemas",
|
|
748
|
+
"fields",
|
|
749
|
+
"create",
|
|
750
|
+
"contacts",
|
|
751
|
+
"--raw",
|
|
752
|
+
'{"type":"schedule","slug":"monthly-recurrence","label":"Monthly Recurrence","referenceField":"c52efe3f-2825-4c60-a7e3-34dd4c1ce043","beginningPeriodField":"d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90","endingPeriodField":"d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90","frequency":"monthly"}',
|
|
753
|
+
]);
|
|
754
|
+
|
|
755
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body))).toEqual({
|
|
756
|
+
type: "ScheduleField",
|
|
757
|
+
slug: "monthly-recurrence",
|
|
758
|
+
label: "Monthly Recurrence",
|
|
759
|
+
config: {
|
|
760
|
+
referenceField: "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
|
|
761
|
+
beginningPeriodField: "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
|
|
762
|
+
endingPeriodField: "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
|
|
763
|
+
frequency: "monthly",
|
|
764
|
+
},
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it("rejects schedule fields with invalid frequency before hitting the backend", async () => {
|
|
769
|
+
const configPath = createApiConfig();
|
|
770
|
+
const output = createStdoutBuffer();
|
|
771
|
+
const fetchMock = createFetchMock();
|
|
772
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
773
|
+
|
|
774
|
+
await expect(
|
|
775
|
+
program.parseAsync([
|
|
776
|
+
"node",
|
|
777
|
+
"zinkee",
|
|
778
|
+
"schemas",
|
|
779
|
+
"fields",
|
|
780
|
+
"create",
|
|
781
|
+
"contacts",
|
|
782
|
+
"--raw",
|
|
783
|
+
'{"type":"ScheduleField","slug":"weekly","label":"Weekly","config":{"referenceField":"c52efe3f-2825-4c60-a7e3-34dd4c1ce043","beginningPeriodField":"d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90","endingPeriodField":"d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90","frequency":"weekly"}}',
|
|
784
|
+
]),
|
|
785
|
+
).rejects.toThrowError(
|
|
786
|
+
'Schedule "frequency" must be "monthly" or "daily" (received "weekly").',
|
|
787
|
+
);
|
|
788
|
+
|
|
789
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
it("rejects schedule fields missing required config fields", async () => {
|
|
793
|
+
const configPath = createApiConfig();
|
|
794
|
+
const output = createStdoutBuffer();
|
|
795
|
+
const fetchMock = createFetchMock();
|
|
796
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
797
|
+
|
|
798
|
+
await expect(
|
|
799
|
+
program.parseAsync([
|
|
800
|
+
"node",
|
|
801
|
+
"zinkee",
|
|
802
|
+
"schemas",
|
|
803
|
+
"fields",
|
|
804
|
+
"create",
|
|
805
|
+
"contacts",
|
|
806
|
+
"--raw",
|
|
807
|
+
'{"type":"ScheduleField","slug":"recurrence","label":"Recurrence","config":{"frequency":"daily"}}',
|
|
808
|
+
]),
|
|
809
|
+
).rejects.toThrowError(
|
|
810
|
+
/Schedule fields require "referenceField", "beginningPeriodField", "endingPeriodField" and "frequency"/,
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
814
|
+
});
|
|
815
|
+
|
|
593
816
|
it("rejects unsupported field types before making network requests", async () => {
|
|
594
817
|
const configPath = createApiConfig();
|
|
595
818
|
const output = createStdoutBuffer();
|
|
@@ -612,7 +835,7 @@ describe("schemas commands", () => {
|
|
|
612
835
|
"Bad",
|
|
613
836
|
]),
|
|
614
837
|
).rejects.toThrowError(
|
|
615
|
-
'Unsupported field type "InvalidField". Valid types: text, number, date, option, file, reference, formula, member, autoincremental.',
|
|
838
|
+
'Unsupported field type "InvalidField". Valid types: text, number, date, option, file, reference, formula, member, autoincremental, schedule.',
|
|
616
839
|
);
|
|
617
840
|
|
|
618
841
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
@@ -626,16 +849,16 @@ describe("schemas commands", () => {
|
|
|
626
849
|
const help = create?.helpInformation() ?? "";
|
|
627
850
|
|
|
628
851
|
expect(help).toContain("--type <type>");
|
|
852
|
+
expect(help).toContain("--default <value>");
|
|
629
853
|
expect(help).toContain("member");
|
|
630
854
|
expect(help).toContain("autoincremental");
|
|
631
855
|
expect(help).toContain("Raw payload shapes by type:");
|
|
632
|
-
expect(help).toContain('"type":"
|
|
856
|
+
expect(help).toContain('"type":"NumberField","slug":"quantity"');
|
|
633
857
|
expect(help).toContain('"defaultValue":"0"');
|
|
634
|
-
expect(help).toContain('"symbolEnabled":true');
|
|
635
858
|
expect(help).toContain('- member: Workspace member field.');
|
|
636
|
-
expect(help).toContain('"type":"
|
|
859
|
+
expect(help).toContain('"type":"MemberField","slug":"owner","label":"Owner"');
|
|
637
860
|
expect(help).toContain('- option: Dropdown/select field with options.');
|
|
638
|
-
expect(help).toContain('"type":"
|
|
861
|
+
expect(help).toContain('"type":"OptionField","slug":"status","label":"Status"');
|
|
639
862
|
expect(help).toContain('Use "zinkee --example schemas fields create --type <type>"');
|
|
640
863
|
});
|
|
641
864
|
|
|
@@ -692,7 +915,7 @@ describe("schemas commands", () => {
|
|
|
692
915
|
'{"type":"option","slug":"status","label":"Status","options":[{"id":1,"name":"Open","color":"default"}]}',
|
|
693
916
|
]),
|
|
694
917
|
).rejects.toThrowError(
|
|
695
|
-
'Invalid payload for field type "option". Expected raw shape: {"type":"
|
|
918
|
+
'Invalid payload for field type "option". Expected raw shape: {"type":"OptionField","slug":"status","label":"Status","config":{"options":[{"id":1,"name":"Open","color":"default"},{"id":2,"name":"Closed","color":"success"}]}}',
|
|
696
919
|
);
|
|
697
920
|
});
|
|
698
921
|
|
package/src/commands/schemas.ts
CHANGED
|
@@ -63,6 +63,7 @@ interface FieldCreateCommandOptions {
|
|
|
63
63
|
slug?: string;
|
|
64
64
|
label?: string;
|
|
65
65
|
required?: boolean;
|
|
66
|
+
default?: string;
|
|
66
67
|
raw?: string;
|
|
67
68
|
rawFile?: string;
|
|
68
69
|
}
|
|
@@ -70,6 +71,7 @@ interface FieldCreateCommandOptions {
|
|
|
70
71
|
interface FieldUpdateCommandOptions {
|
|
71
72
|
label?: string;
|
|
72
73
|
required?: boolean;
|
|
74
|
+
default?: string;
|
|
73
75
|
raw?: string;
|
|
74
76
|
rawFile?: string;
|
|
75
77
|
}
|
|
@@ -301,6 +303,29 @@ function buildSchemaUpdateInput(options: SchemaUpdateCommandOptions): UpdateSche
|
|
|
301
303
|
return input;
|
|
302
304
|
}
|
|
303
305
|
|
|
306
|
+
function applyFieldDefaultValue(
|
|
307
|
+
input: CreateSchemaFieldInput | UpdateSchemaFieldInput,
|
|
308
|
+
defaultValue: string | undefined,
|
|
309
|
+
): void {
|
|
310
|
+
if (defaultValue === undefined) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const config = input.config;
|
|
315
|
+
|
|
316
|
+
if (config !== undefined && !isJsonObject(config)) {
|
|
317
|
+
throw new CliError(
|
|
318
|
+
"invalid_cli_usage",
|
|
319
|
+
'Expected field "config" to be a JSON object when using "--default".',
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
input.config = {
|
|
324
|
+
...(config ?? {}),
|
|
325
|
+
defaultValue,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
304
329
|
function buildFieldCreateInput(
|
|
305
330
|
options: FieldCreateCommandOptions,
|
|
306
331
|
): CreateSchemaFieldInput {
|
|
@@ -337,6 +362,8 @@ function buildFieldCreateInput(
|
|
|
337
362
|
input.required = options.required;
|
|
338
363
|
}
|
|
339
364
|
|
|
365
|
+
applyFieldDefaultValue(input, options.default);
|
|
366
|
+
|
|
340
367
|
if ("config" in input && isJsonObject(input.config) && isEmptyObject(input.config)) {
|
|
341
368
|
delete input.config;
|
|
342
369
|
}
|
|
@@ -411,6 +438,8 @@ function buildFieldUpdateInput(
|
|
|
411
438
|
input.required = options.required;
|
|
412
439
|
}
|
|
413
440
|
|
|
441
|
+
applyFieldDefaultValue(input, options.default);
|
|
442
|
+
|
|
414
443
|
return input;
|
|
415
444
|
}
|
|
416
445
|
|
|
@@ -640,6 +669,7 @@ export function registerSchemasCommands(
|
|
|
640
669
|
.option("--slug <slug>", "field slug")
|
|
641
670
|
.option("--label <label>", "field label")
|
|
642
671
|
.option("--required <bool>", "required flag", parseBooleanFlag)
|
|
672
|
+
.option("--default <value>", "default field value")
|
|
643
673
|
.option("--raw <json>", 'raw JSON object payload (see type-specific shapes in --help)')
|
|
644
674
|
.option("--raw-file <path>", "path to a raw JSON object payload")
|
|
645
675
|
.action(async function action(
|
|
@@ -673,6 +703,7 @@ export function registerSchemasCommands(
|
|
|
673
703
|
.argument("<field>", "field uuid or slug", parseUuidOrSlugSelector)
|
|
674
704
|
.option("--label <label>", "field label")
|
|
675
705
|
.option("--required <bool>", "required flag", parseBooleanFlag)
|
|
706
|
+
.option("--default <value>", "default field value")
|
|
676
707
|
.option("--raw <json>", "raw JSON object payload")
|
|
677
708
|
.option("--raw-file <path>", "path to a raw JSON object payload")
|
|
678
709
|
.action(async function action(
|
package/src/index.test.ts
CHANGED
|
@@ -85,12 +85,12 @@ describe("project bootstrap", () => {
|
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
expect(output).toContain('zinkee --json schemas fields create contacts --raw');
|
|
88
|
-
expect(output).toContain('"type":"
|
|
89
|
-
expect(output).not.toContain('"type":"
|
|
90
|
-
expect(output).toContain("
|
|
88
|
+
expect(output).toContain('"type":"OptionField"');
|
|
89
|
+
expect(output).not.toContain('"type":"TextField"');
|
|
90
|
+
expect(output).toContain("Option field (select dropdown)");
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
it("shows
|
|
93
|
+
it("shows every backend number variant, including currency formatting", async () => {
|
|
94
94
|
let output = "";
|
|
95
95
|
|
|
96
96
|
await showExamples(
|
|
@@ -105,9 +105,15 @@ describe("project bootstrap", () => {
|
|
|
105
105
|
{ type: "number" },
|
|
106
106
|
);
|
|
107
107
|
|
|
108
|
-
expect(output).toContain('"slug":"
|
|
108
|
+
expect(output).toContain('"slug":"quantity"');
|
|
109
|
+
expect(output).toContain('"slug":"amount-eur"');
|
|
110
|
+
expect(output).toContain('"slug":"amount-usd"');
|
|
111
|
+
expect(output).toContain('"defaultValue":"0"');
|
|
109
112
|
expect(output).toContain('"symbolEnabled":true');
|
|
113
|
+
expect(output).toContain('"symbolPosition":"before"');
|
|
110
114
|
expect(output).toContain('"symbolPosition":"right"');
|
|
115
|
+
expect(output).toContain('"format":"percentage"');
|
|
116
|
+
expect(output).toContain('"durationFormat":"HH:MM"');
|
|
111
117
|
});
|
|
112
118
|
|
|
113
119
|
it("shows automation examples with descriptions and equivalent payloads", async () => {
|
|
@@ -21,18 +21,54 @@ describe("getCommandExamples", () => {
|
|
|
21
21
|
const examples = getCommandExamples("schemas fields create", { type: "member" });
|
|
22
22
|
|
|
23
23
|
expect(examples).toHaveLength(1);
|
|
24
|
-
expect(examples[0]?.command).toContain('"type":"
|
|
25
|
-
expect(examples[0]?.description).
|
|
24
|
+
expect(examples[0]?.command).toContain('"type":"MemberField"');
|
|
25
|
+
expect(examples[0]?.description).toBe("Member field");
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
it("returns
|
|
29
|
-
const examples = getCommandExamples("schemas fields create", { type: "
|
|
28
|
+
it("returns monthly and daily variants for schedule fields", () => {
|
|
29
|
+
const examples = getCommandExamples("schemas fields create", { type: "schedule" });
|
|
30
30
|
|
|
31
31
|
expect(examples).toHaveLength(2);
|
|
32
|
-
expect(examples
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
expect(examples.map((example) => example.description)).toEqual([
|
|
33
|
+
"Schedule field (monthly)",
|
|
34
|
+
"Schedule field (daily)",
|
|
35
|
+
]);
|
|
36
|
+
expect(examples[0]?.command).toContain('"type":"ScheduleField"');
|
|
37
|
+
expect(examples[0]?.command).toContain('"frequency":"monthly"');
|
|
38
|
+
expect(examples[1]?.command).toContain('"frequency":"daily"');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("covers every backend number field variant", () => {
|
|
42
|
+
const examples = getCommandExamples("schemas fields create", { type: "number" });
|
|
43
|
+
|
|
44
|
+
expect(examples).toHaveLength(6);
|
|
45
|
+
|
|
46
|
+
const descriptions = examples.map((example) => example.description);
|
|
47
|
+
expect(descriptions).toEqual([
|
|
48
|
+
"Number field (plain integer)",
|
|
49
|
+
"Number field (decimal with thousands)",
|
|
50
|
+
"Number field (currency € prefix)",
|
|
51
|
+
"Number field (currency $ suffix)",
|
|
52
|
+
"Number field (percentage)",
|
|
53
|
+
"Number field (duration HH:MM)",
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
expect(examples[0]?.command).toContain('"slug":"quantity"');
|
|
57
|
+
expect(examples[0]?.command).toContain('"format":"number"');
|
|
58
|
+
|
|
59
|
+
const eurCurrency = examples[2]?.command ?? "";
|
|
60
|
+
expect(eurCurrency).toContain('"format":"currency"');
|
|
61
|
+
expect(eurCurrency).toContain('"symbolEnabled":true');
|
|
62
|
+
expect(eurCurrency).toContain('"symbolPosition":"before"');
|
|
63
|
+
expect(eurCurrency).toContain('"symbolValue":"€"');
|
|
64
|
+
|
|
65
|
+
const usdCurrency = examples[3]?.command ?? "";
|
|
66
|
+
expect(usdCurrency).toContain('"symbolPosition":"right"');
|
|
67
|
+
expect(usdCurrency).toContain('"symbolValue":" $"');
|
|
68
|
+
|
|
69
|
+
expect(examples[4]?.command).toContain('"format":"percentage"');
|
|
70
|
+
expect(examples[5]?.command).toContain('"format":"duration"');
|
|
71
|
+
expect(examples[5]?.command).toContain('"durationFormat":"HH:MM"');
|
|
36
72
|
});
|
|
37
73
|
|
|
38
74
|
it("returns canonical automation creation examples", () => {
|