text-guitar-chart 0.0.1

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,1086 @@
1
+ // @ts-check
2
+
3
+ import { describe, test } from "node:test";
4
+ import assert from "node:assert/strict";
5
+ import stringToFingering from "../lib/stringToFingering.js";
6
+
7
+ /**
8
+ * @param {import("svguitar").Finger} fingerA
9
+ * @param {import("svguitar").Finger} fingerB
10
+ * @returns {boolean}
11
+ */
12
+ const isFingerEqual = (fingerA, fingerB) => {
13
+ if (!(fingerA[0] === fingerB[0] && fingerA[1] === fingerB[1])) {
14
+ return false;
15
+ }
16
+ if (typeof fingerA[2] === "object" && typeof fingerB[2] === "object") {
17
+ return (
18
+ fingerA[2].color === fingerB[2].color &&
19
+ fingerA[2].text === fingerB[2].text
20
+ );
21
+ }
22
+ return fingerA === fingerB;
23
+ };
24
+
25
+ /**
26
+ * @param {import("svguitar").Chord | null} result
27
+ * @param {Array<import("svguitar").Finger>} fingers
28
+ * @returns {boolean}
29
+ */
30
+ function fingersContains(result, fingers) {
31
+ if (!result) {
32
+ return false;
33
+ }
34
+ if (fingers.length !== result.fingers.length) {
35
+ return false;
36
+ }
37
+ for (const finger of fingers) {
38
+ const found = result.fingers.find((f) => isFingerEqual(f, finger));
39
+ if (!found) {
40
+ return false;
41
+ }
42
+ }
43
+ return true;
44
+ }
45
+
46
+ describe("stringToFingering", () => {
47
+ describe("ASCII format parsing", () => {
48
+ test("parses simple A minor chord (open strings with dots)", () => {
49
+ const fingeringStr = ` A min
50
+ ######
51
+ oo o
52
+ ------
53
+ ||||o|
54
+ ||o*||
55
+ ||||||`;
56
+
57
+ const result = stringToFingering(fingeringStr);
58
+ assert.equal(
59
+ fingersContains(result, [
60
+ [
61
+ 2,
62
+ 1,
63
+ {
64
+ text: "",
65
+ color: "#000000",
66
+ },
67
+ ],
68
+ [
69
+ 3,
70
+ 2,
71
+ {
72
+ text: "",
73
+ color: "#e74c3c",
74
+ },
75
+ ],
76
+ [
77
+ 4,
78
+ 2,
79
+ {
80
+ text: "",
81
+ color: "#000000",
82
+ },
83
+ ],
84
+ [5, 0, { text: "", color: "#000000" }],
85
+ [6, 0, { text: "", color: "#000000" }],
86
+ [1, 0, { text: "", color: "#000000" }],
87
+ ]),
88
+ true,
89
+ "Fingering does not match expected A minor chord"
90
+ );
91
+ assert.equal(
92
+ result?.title,
93
+ "A min",
94
+ "Title does not match expected A min"
95
+ );
96
+ assert.equal(
97
+ result?.position,
98
+ undefined,
99
+ "Position does not match expected undefined"
100
+ );
101
+ assert.deepEqual(result.barres, [], "Barres should be empty");
102
+ });
103
+
104
+ test("parses D chord with muted strings", () => {
105
+ const fingeringStr = ` D
106
+ ######
107
+ xoo
108
+ ------
109
+ ||||||
110
+ |||o|o
111
+ ||||*|`;
112
+
113
+ const result = stringToFingering(fingeringStr);
114
+
115
+ assert.equal(
116
+ fingersContains(result, [
117
+ [
118
+ 1,
119
+ 2,
120
+ {
121
+ text: "",
122
+ color: "#000000",
123
+ },
124
+ ],
125
+ [
126
+ 3,
127
+ 2,
128
+ {
129
+ text: "",
130
+ color: "#000000",
131
+ },
132
+ ],
133
+ [
134
+ 2,
135
+ 3,
136
+ {
137
+ text: "",
138
+ color: "#e74c3c",
139
+ },
140
+ ],
141
+ [6, "x", {text: "", color: "#000000"}],
142
+ [5, 0, {text: "", color: "#000000"}],
143
+ [4, 0, {text: "", color: "#000000"}],
144
+ ]),
145
+ true,
146
+ "Fingering does not match expected D chord"
147
+ );
148
+ assert.equal(result?.title, "D", "Title does not match expected D");
149
+ assert.equal(
150
+ result?.position,
151
+ undefined,
152
+ "Position does not match expected undefined"
153
+ );
154
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
155
+ });
156
+
157
+ test("parses G7 chord with starting fret number", () => {
158
+ const fingeringStr = ` G 7
159
+ ######
160
+ xx
161
+ ------
162
+ 5||*|||
163
+ ||||o|
164
+ |||o|o`;
165
+
166
+ const result = stringToFingering(fingeringStr);
167
+
168
+ assert.equal(
169
+ fingersContains(result, [
170
+ [6, "x", {text: "", color: "#000000"}],
171
+ [5, "x", {text: "", color: "#000000"}],
172
+ [
173
+ 4,
174
+ 1,
175
+ {
176
+ text: "",
177
+ color: "#e74c3c",
178
+ },
179
+ ],
180
+ [
181
+ 2,
182
+ 2,
183
+ {
184
+ text: "",
185
+ color: "#000000",
186
+ },
187
+ ],
188
+ [
189
+ 3,
190
+ 3,
191
+ {
192
+ text: "",
193
+ color: "#000000",
194
+ },
195
+ ],
196
+ [
197
+ 1,
198
+ 3,
199
+ {
200
+ text: "",
201
+ color: "#000000",
202
+ },
203
+ ],
204
+ ]),
205
+ true,
206
+ "Fingering does not match expected G7 chord"
207
+ );
208
+ assert.equal(result?.title, "G 7", "Title does not match expected G 7");
209
+ assert.equal(result?.position, 5, "Position does not match expected 5");
210
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
211
+ });
212
+
213
+ test("parses G7 chord with starting fret number 1", () => {
214
+ const fingeringStr = ` G 7
215
+ ######
216
+ xx
217
+ ------
218
+ 1||*|||
219
+ ||||o|
220
+ |||o|o`;
221
+
222
+ const result = stringToFingering(fingeringStr);
223
+
224
+ assert.equal(
225
+ fingersContains(result, [
226
+ [6, "x", {text: "", color: "#000000"}],
227
+ [5, "x", {text: "", color: "#000000"}],
228
+ [
229
+ 4,
230
+ 1,
231
+ {
232
+ text: "",
233
+ color: "#e74c3c",
234
+ },
235
+ ],
236
+ [
237
+ 2,
238
+ 2,
239
+ {
240
+ text: "",
241
+ color: "#000000",
242
+ },
243
+ ],
244
+ [
245
+ 3,
246
+ 3,
247
+ {
248
+ text: "",
249
+ color: "#000000",
250
+ },
251
+ ],
252
+ [
253
+ 1,
254
+ 3,
255
+ {
256
+ text: "",
257
+ color: "#000000",
258
+ },
259
+ ],
260
+ ]),
261
+ true,
262
+ "Fingering does not match expected G7 chord"
263
+ );
264
+ assert.equal(result?.title, "G 7", "Title does not match expected G 7");
265
+ assert.equal(result?.position, 1, "Position does not match expected 1");
266
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
267
+ });
268
+
269
+ test("parses G7 chord with starting fret number 1 (alternate version)", () => {
270
+ const fingeringStr = ` G 7
271
+ ######
272
+ xx
273
+ ======
274
+ ||*|||
275
+ ||||o|
276
+ |||o|o`;
277
+
278
+ const result = stringToFingering(fingeringStr);
279
+
280
+ assert.equal(
281
+ fingersContains(result, [
282
+ [6, "x", {text: "", color: "#000000"}],
283
+ [5, "x", {text: "", color: "#000000"}],
284
+ [
285
+ 4,
286
+ 1,
287
+ {
288
+ text: "",
289
+ color: "#e74c3c",
290
+ },
291
+ ],
292
+ [
293
+ 2,
294
+ 2,
295
+ {
296
+ text: "",
297
+ color: "#000000",
298
+ },
299
+ ],
300
+ [
301
+ 3,
302
+ 3,
303
+ {
304
+ text: "",
305
+ color: "#000000",
306
+ },
307
+ ],
308
+ [
309
+ 1,
310
+ 3,
311
+ {
312
+ text: "",
313
+ color: "#000000",
314
+ },
315
+ ],
316
+ ]),
317
+ true,
318
+ "Fingering does not match expected G7 chord"
319
+ );
320
+ assert.equal(result?.title, "G 7", "Title does not match expected G 7");
321
+ assert.equal(result?.position, 1, "Position does not match expected 1");
322
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
323
+ });
324
+
325
+ test("parses G7 chord with 2 digits starting fret number", () => {
326
+ const fingeringStr = ` G 7
327
+ ######
328
+ xx
329
+ ------
330
+ 15||*|||
331
+ ||||o|
332
+ |||o|o`;
333
+
334
+ const result = stringToFingering(fingeringStr);
335
+
336
+ assert.equal(
337
+ fingersContains(result, [
338
+ [6, "x", {text: "", color: "#000000"}],
339
+ [5, "x", {text: "", color: "#000000"}],
340
+ [
341
+ 4,
342
+ 1,
343
+ {
344
+ text: "",
345
+ color: "#e74c3c",
346
+ },
347
+ ],
348
+ [
349
+ 2,
350
+ 2,
351
+ {
352
+ text: "",
353
+ color: "#000000",
354
+ },
355
+ ],
356
+ [
357
+ 3,
358
+ 3,
359
+ {
360
+ text: "",
361
+ color: "#000000",
362
+ },
363
+ ],
364
+ [
365
+ 1,
366
+ 3,
367
+ {
368
+ text: "",
369
+ color: "#000000",
370
+ },
371
+ ],
372
+ ]),
373
+ true,
374
+ "Fingering does not match expected G7 chord"
375
+ );
376
+ assert.equal(result?.title, "G 7", "Title does not match expected G 7");
377
+ assert.equal(result?.position, 15, "Position does not match expected 15");
378
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
379
+ });
380
+
381
+ test("parses E dom 7 chord with finger numbers", () => {
382
+ const fingeringStr = ` E dom 7
383
+ #######
384
+ x x
385
+ ------
386
+ |||3||
387
+ |51|||
388
+ ||||7|`;
389
+
390
+ const result = stringToFingering(fingeringStr);
391
+ assert.equal(
392
+ fingersContains(result, [
393
+ [
394
+ 5,
395
+ 2,
396
+ {
397
+ text: "5",
398
+ color: "#000000",
399
+ },
400
+ ],
401
+ [
402
+ 4,
403
+ 2,
404
+ {
405
+ text: "1",
406
+ color: "#000000",
407
+ },
408
+ ],
409
+ [
410
+ 3,
411
+ 1,
412
+ {
413
+ text: "3",
414
+ color: "#000000",
415
+ },
416
+ ],
417
+ [
418
+ 2,
419
+ 3,
420
+ {
421
+ text: "7",
422
+ color: "#000000",
423
+ },
424
+ ],
425
+ [6, "x", {text: "", color: "#000000"}],
426
+ [1, "x", {text: "", color: "#000000"}],
427
+ ]),
428
+ true,
429
+ "Fingering does not match expected E dom 7 chord with finger numbers"
430
+ );
431
+ assert.equal(
432
+ result?.title,
433
+ "E dom 7",
434
+ "Title does not match expected E dom 7"
435
+ );
436
+ assert.equal(
437
+ result?.position,
438
+ undefined,
439
+ "Position does not match expected undefined"
440
+ );
441
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
442
+ });
443
+ });
444
+
445
+ describe("Unicode format parsing", () => {
446
+ test("parses A minor chord (Unicode format)", () => {
447
+ const fingeringStr = ` A min
448
+ ‾‾‾‾‾‾‾‾‾‾‾
449
+ ○ ○ ○
450
+ ┌─┬─┬─┬─┬─┐
451
+ │ │ │ │ ○ │
452
+ ├─┼─┼─┼─┼─┤
453
+ │ │ ○ ● │ │
454
+ ├─┼─┼─┼─┼─┤
455
+ │ │ │ │ │ │
456
+ └─┴─┴─┴─┴─┘`;
457
+
458
+ const result = stringToFingering(fingeringStr);
459
+ assert.equal(
460
+ fingersContains(result, [
461
+ [
462
+ 2,
463
+ 1,
464
+ {
465
+ text: "",
466
+ color: "#000000",
467
+ },
468
+ ],
469
+ [
470
+ 3,
471
+ 2,
472
+ {
473
+ text: "",
474
+ color: "#e74c3c",
475
+ },
476
+ ],
477
+ [
478
+ 4,
479
+ 2,
480
+ {
481
+ text: "",
482
+ color: "#000000",
483
+ },
484
+ ],
485
+ [5, 0, { text: "", color: "#000000" }],
486
+ [6, 0, { text: "", color: "#000000" }],
487
+ [1, 0, { text: "", color: "#000000" }],
488
+ ]),
489
+ true,
490
+ "Fingering does not match expected A minor chord"
491
+ );
492
+ assert.equal(
493
+ result?.title,
494
+ "A min",
495
+ "Title does not match expected A min"
496
+ );
497
+ assert.equal(
498
+ result?.position,
499
+ undefined,
500
+ "Position does not match expected undefined"
501
+ );
502
+ assert.deepEqual(result?.barres, [], "Barres should be empty"); });
503
+ test("parses D chord (Unicode format)", () => {
504
+ const fingeringStr = ` D
505
+ ‾‾‾‾‾‾‾‾‾‾‾
506
+ × ○ ○
507
+ ┌─┬─┬─┬─┬─┐
508
+ │ │ │ │ │ │
509
+ ├─┼─┼─┼─┼─┤
510
+ │ │ │ ○ │ ○
511
+ ├─┼─┼─┼─┼─┤
512
+ │ │ │ │ ● │
513
+ └─┴─┴─┴─┴─┘`;
514
+
515
+ const result = stringToFingering(fingeringStr);
516
+
517
+ assert.equal(
518
+ fingersContains(result, [
519
+ [
520
+ 1,
521
+ 2,
522
+ {
523
+ text: "",
524
+ color: "#000000",
525
+ },
526
+ ],
527
+ [
528
+ 3,
529
+ 2,
530
+ {
531
+ text: "",
532
+ color: "#000000",
533
+ },
534
+ ],
535
+ [
536
+ 2,
537
+ 3,
538
+ {
539
+ text: "",
540
+ color: "#e74c3c",
541
+ },
542
+ ],
543
+ [6, "x", {text: "", color: "#000000"}],
544
+ [5, 0, {text: "", color: "#000000"}],
545
+ [4, 0, {text: "", color: "#000000"}],
546
+ ]),
547
+ true,
548
+ "Fingering does not match expected D chord"
549
+ );
550
+ assert.equal(result?.title, "D", "Title does not match expected D");
551
+ assert.equal(
552
+ result?.position,
553
+ undefined,
554
+ "Position does not match expected undefined"
555
+ );
556
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
557
+ });
558
+
559
+ test("parses G7 chord with starting fret (Unicode format)", () => {
560
+ const fingeringStr = ` G 7
561
+ ‾‾‾‾‾‾‾‾‾‾‾
562
+ × ×
563
+ ┌─┬─┬─┬─┬─┐
564
+ 5│ │ ● │ │ │
565
+ ├─┼─┼─┼─┼─┤
566
+ │ │ │ │ ○ │
567
+ ├─┼─┼─┼─┼─┤
568
+ │ │ │ ○ │ ○
569
+ └─┴─┴─┴─┴─┘`;
570
+
571
+ const result = stringToFingering(fingeringStr);
572
+
573
+ assert.equal(
574
+ fingersContains(result, [
575
+ [6, "x", {text: "", color: "#000000"}],
576
+ [5, "x", {text: "", color: "#000000"}],
577
+ [
578
+ 4,
579
+ 1,
580
+ {
581
+ text: "",
582
+ color: "#e74c3c",
583
+ },
584
+ ],
585
+ [
586
+ 2,
587
+ 2,
588
+ {
589
+ text: "",
590
+ color: "#000000",
591
+ },
592
+ ],
593
+ [
594
+ 3,
595
+ 3,
596
+ {
597
+ text: "",
598
+ color: "#000000",
599
+ },
600
+ ],
601
+ [
602
+ 1,
603
+ 3,
604
+ {
605
+ text: "",
606
+ color: "#000000",
607
+ },
608
+ ],
609
+ ]),
610
+ true,
611
+ "Fingering does not match expected G7 chord"
612
+ );
613
+ assert.equal(result?.title, "G 7", "Title does not match expected G 7");
614
+ assert.equal(result?.position, 5, "Position does not match expected 5");
615
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
616
+ });
617
+
618
+ test("parses G7 chord with 2 digits starting fret (Unicode format)", () => {
619
+ const fingeringStr = ` G 7
620
+ ‾‾‾‾‾‾‾‾‾‾‾
621
+ × ×
622
+ ┌─┬─┬─┬─┬─┐
623
+ 15│ │ ● │ │ │
624
+ ├─┼─┼─┼─┼─┤
625
+ │ │ │ │ ○ │
626
+ ├─┼─┼─┼─┼─┤
627
+ │ │ │ ○ │ ○
628
+ └─┴─┴─┴─┴─┘`;
629
+
630
+ const result = stringToFingering(fingeringStr);
631
+
632
+ assert.equal(
633
+ fingersContains(result, [
634
+ [6, "x", {text: "", color: "#000000"}],
635
+ [5, "x", {text: "", color: "#000000"}],
636
+ [
637
+ 4,
638
+ 1,
639
+ {
640
+ text: "",
641
+ color: "#e74c3c",
642
+ },
643
+ ],
644
+ [
645
+ 2,
646
+ 2,
647
+ {
648
+ text: "",
649
+ color: "#000000",
650
+ },
651
+ ],
652
+ [
653
+ 3,
654
+ 3,
655
+ {
656
+ text: "",
657
+ color: "#000000",
658
+ },
659
+ ],
660
+ [
661
+ 1,
662
+ 3,
663
+ {
664
+ text: "",
665
+ color: "#000000",
666
+ },
667
+ ],
668
+ ]),
669
+ true,
670
+ "Fingering does not match expected G7 chord"
671
+ );
672
+ assert.equal(result?.title, "G 7", "Title does not match expected G 7");
673
+ assert.equal(result?.position, 15, "Position does not match expected 15");
674
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
675
+ });
676
+
677
+ test("parses G7 chord with 1 starting fret (Unicode format)", () => {
678
+ const fingeringStr = ` G 7
679
+ ‾‾‾‾‾‾‾‾‾‾‾
680
+ × ×
681
+ ┌─┬─┬─┬─┬─┐
682
+ 1│ │ ● │ │ │
683
+ ├─┼─┼─┼─┼─┤
684
+ │ │ │ │ ○ │
685
+ ├─┼─┼─┼─┼─┤
686
+ │ │ │ ○ │ ○
687
+ └─┴─┴─┴─┴─┘`;
688
+
689
+ const result = stringToFingering(fingeringStr);
690
+
691
+ assert.equal(
692
+ fingersContains(result, [
693
+ [6, "x", {text: "", color: "#000000"}],
694
+ [5, "x", {text: "", color: "#000000"}],
695
+ [
696
+ 4,
697
+ 1,
698
+ {
699
+ text: "",
700
+ color: "#e74c3c",
701
+ },
702
+ ],
703
+ [
704
+ 2,
705
+ 2,
706
+ {
707
+ text: "",
708
+ color: "#000000",
709
+ },
710
+ ],
711
+ [
712
+ 3,
713
+ 3,
714
+ {
715
+ text: "",
716
+ color: "#000000",
717
+ },
718
+ ],
719
+ [
720
+ 1,
721
+ 3,
722
+ {
723
+ text: "",
724
+ color: "#000000",
725
+ },
726
+ ],
727
+ ]),
728
+ true,
729
+ "Fingering does not match expected G7 chord"
730
+ );
731
+ assert.equal(result?.title, "G 7", "Title does not match expected G 7");
732
+ assert.equal(result?.position, 1, "Position does not match expected 1");
733
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
734
+ });
735
+
736
+ test("parses G7 chord with 1 starting fret (Unicode format) - alternate version", () => {
737
+ const fingeringStr = ` G 7
738
+ ‾‾‾‾‾‾‾‾‾‾‾
739
+ × ×
740
+ ╒═╤═╤═╤═╤═╕
741
+ │ │ ● │ │ │
742
+ ├─┼─┼─┼─┼─┤
743
+ │ │ │ │ ○ │
744
+ ├─┼─┼─┼─┼─┤
745
+ │ │ │ ○ │ ○
746
+ └─┴─┴─┴─┴─┘`;
747
+
748
+ const result = stringToFingering(fingeringStr);
749
+
750
+ assert.equal(
751
+ fingersContains(result, [
752
+ [6, "x", {text: "", color: "#000000"}],
753
+ [5, "x", {text: "", color: "#000000"}],
754
+ [
755
+ 4,
756
+ 1,
757
+ {
758
+ text: "",
759
+ color: "#e74c3c",
760
+ },
761
+ ],
762
+ [
763
+ 2,
764
+ 2,
765
+ {
766
+ text: "",
767
+ color: "#000000",
768
+ },
769
+ ],
770
+ [
771
+ 3,
772
+ 3,
773
+ {
774
+ text: "",
775
+ color: "#000000",
776
+ },
777
+ ],
778
+ [
779
+ 1,
780
+ 3,
781
+ {
782
+ text: "",
783
+ color: "#000000",
784
+ },
785
+ ],
786
+ ]),
787
+ true,
788
+ "Fingering does not match expected G7 chord"
789
+ );
790
+ assert.equal(result?.title, "G 7", "Title does not match expected G 7");
791
+ assert.equal(result?.position, 1, "Position does not match expected 1");
792
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
793
+ });
794
+
795
+ test("parses E dom 7 with finger numbers (Unicode format)", () => {
796
+ const fingeringStr = ` E dom 7
797
+ ‾‾‾‾‾‾‾‾‾‾‾
798
+ × ×
799
+ ┌─┬─┬─┬─┬─┐
800
+ │ │ │ 3 │ │
801
+ ├─┼─┼─┼─┼─┤
802
+ │ 5 1 │ │ │
803
+ ├─┼─┼─┼─┼─┤
804
+ │ │ │ │ 7 │
805
+ └─┴─┴─┴─┴─┘`;
806
+
807
+ const result = stringToFingering(fingeringStr);
808
+ assert.equal(
809
+ fingersContains(result, [
810
+ [
811
+ 5,
812
+ 2,
813
+ {
814
+ text: "5",
815
+ color: "#000000",
816
+ },
817
+ ],
818
+ [
819
+ 4,
820
+ 2,
821
+ {
822
+ text: "1",
823
+ color: "#000000",
824
+ },
825
+ ],
826
+ [
827
+ 3,
828
+ 1,
829
+ {
830
+ text: "3",
831
+ color: "#000000",
832
+ },
833
+ ],
834
+ [
835
+ 2,
836
+ 3,
837
+ {
838
+ text: "7",
839
+ color: "#000000",
840
+ },
841
+ ],
842
+ [6, "x", {text: "", color: "#000000"}],
843
+ [1, "x", {text: "", color: "#000000"}],
844
+ ]),
845
+ true,
846
+ "Fingering does not match expected E dom 7 chord with finger numbers"
847
+ );
848
+ assert.equal(
849
+ result?.title,
850
+ "E dom 7",
851
+ "Title does not match expected E dom 7"
852
+ );
853
+ assert.equal(
854
+ result?.position,
855
+ undefined,
856
+ "Position does not match expected undefined"
857
+ );
858
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
859
+ });
860
+ });
861
+
862
+ describe("Edge cases and variations", () => {
863
+ test("handles empty fingering string", () => {
864
+ const result = stringToFingering("");
865
+
866
+ assert.equal(result, null, "Result should be null for empty string");
867
+ });
868
+
869
+ test("handles fingering with no title", () => {
870
+ const fingeringStr = ` oo o
871
+ ------
872
+ ||||o|
873
+ ||o*||
874
+ ||||||`;
875
+ const result = stringToFingering(fingeringStr);
876
+
877
+ assert.equal(
878
+ fingersContains(result, [
879
+ [
880
+ 2,
881
+ 1,
882
+ {
883
+ text: "",
884
+ color: "#000000",
885
+ },
886
+ ],
887
+ [
888
+ 3,
889
+ 2,
890
+ {
891
+ text: "",
892
+ color: "#e74c3c",
893
+ },
894
+ ],
895
+ [
896
+ 4,
897
+ 2,
898
+ {
899
+ text: "",
900
+ color: "#000000",
901
+ },
902
+ ],
903
+ [5, 0, { text: "", color: "#000000" }],
904
+ [6, 0, { text: "", color: "#000000" }],
905
+ [1, 0, { text: "", color: "#000000" }],
906
+ ]),
907
+ true,
908
+ "Fingering does not match expected A minor chord"
909
+ );
910
+ assert.equal(
911
+ result?.title,
912
+ "",
913
+ "Title should be empty"
914
+ );
915
+ assert.equal(
916
+ result?.position,
917
+ undefined,
918
+ "Position does not match expected undefined"
919
+ );
920
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
921
+ });
922
+
923
+ test("handles all open strings", () => {
924
+ const fingeringStr = ` E major
925
+ ######
926
+ oooooo
927
+ ------
928
+ ||||||
929
+ ||||||
930
+ ||||||`;
931
+
932
+ const result = stringToFingering(fingeringStr);
933
+
934
+ assert.equal(
935
+ fingersContains(result, [
936
+ [1, 0, { text: "", color: "#000000" }],
937
+ [2, 0, { text: "", color: "#000000" }],
938
+ [3, 0, { text: "", color: "#000000" }],
939
+ [4, 0, { text: "", color: "#000000" }],
940
+ [5, 0, { text: "", color: "#000000" }],
941
+ [6, 0, { text: "", color: "#000000" }],
942
+ ]),
943
+ true,
944
+ "Fingering does not match expected E major"
945
+ );
946
+ assert.equal(
947
+ result?.title,
948
+ "E major",
949
+ "Title does not match expected E major"
950
+ );
951
+ assert.equal(
952
+ result?.position,
953
+ undefined,
954
+ "Position does not match expected undefined"
955
+ );
956
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
957
+ });
958
+
959
+ test("handles all muted strings", () => {
960
+ const fingeringStr = ` Muted
961
+ ######
962
+ xxxxxx
963
+ ------
964
+ ||||||
965
+ ||||||
966
+ ||||||`;
967
+
968
+ const result = stringToFingering(fingeringStr);
969
+
970
+ assert.equal(
971
+ fingersContains(result, [
972
+ [1, 'x', { text: "", color: "#000000" }],
973
+ [2, 'x', { text: "", color: "#000000" }],
974
+ [3, 'x', { text: "", color: "#000000" }],
975
+ [4, 'x', { text: "", color: "#000000" }],
976
+ [5, 'x', { text: "", color: "#000000" }],
977
+ [6, 'x', { text: "", color: "#000000" }],
978
+ ]), true,
979
+ "Fingering does not match expected Muted strings"
980
+ );
981
+ assert.equal(
982
+ result?.title,
983
+ "Muted",
984
+ "Title does not match expected Muted"
985
+ );
986
+ assert.equal(
987
+ result?.position,
988
+ undefined,
989
+ "Position does not match expected undefined"
990
+ );
991
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
992
+ });
993
+
994
+ test("handles no indications for open strings", () => {
995
+ const fingeringStr = ` Muted
996
+ ######
997
+ ||||||
998
+ ||||||
999
+ ||||||`;
1000
+
1001
+ const result = stringToFingering(fingeringStr);
1002
+ assert.equal(
1003
+ fingersContains(result, []),
1004
+ true,
1005
+ "Fingering does not match expected Muted strings"
1006
+ );
1007
+ assert.equal(
1008
+ result?.title,
1009
+ "Muted",
1010
+ "Title does not match expected Muted"
1011
+ );
1012
+ assert.equal(
1013
+ result?.position,
1014
+ undefined,
1015
+ "Position does not match expected undefined"
1016
+ );
1017
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
1018
+ });
1019
+
1020
+ test("handles no title or open strings (bug 1)", () => {
1021
+ const fingeringStr = ` ------
1022
+ oooooo
1023
+ ||||||
1024
+ ||||||`;
1025
+
1026
+ const result = stringToFingering(fingeringStr);
1027
+ assert.equal(
1028
+ fingersContains(result, [
1029
+ [1, 1, { text: "", color: "#000000" }],
1030
+ [2, 1, { text: "", color: "#000000" }],
1031
+ [3, 1, { text: "", color: "#000000" }],
1032
+ [4, 1, { text: "", color: "#000000" }],
1033
+ [5, 1, { text: "", color: "#000000" }],
1034
+ [6, 1, { text: "", color: "#000000" }],
1035
+ ]), true,
1036
+ "Fingering does not match expected strings"
1037
+ );
1038
+ assert.equal(
1039
+ result?.title,
1040
+ "",
1041
+ "Title should be undefined"
1042
+ );
1043
+ assert.equal(
1044
+ result?.position,
1045
+ undefined,
1046
+ "Position does not match expected undefined"
1047
+ );
1048
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
1049
+ });
1050
+
1051
+ test("handles no title or open strings in unicode (bug 2)", () => {
1052
+ const fingeringStr = ` ┌─┬─┬─┬─┬─┐
1053
+ ○ ○ ○ ○ ○ ○
1054
+ ├─┼─┼─┼─┼─┤
1055
+ │ │ │ │ │ │
1056
+ ├─┼─┼─┼─┼─┤
1057
+ │ │ │ │ │ │
1058
+ └─┴─┴─┴─┴─┘`;
1059
+
1060
+ const result = stringToFingering(fingeringStr);
1061
+ assert.equal(
1062
+ fingersContains(result, [
1063
+ [1, 1, { text: "", color: "#000000" }],
1064
+ [2, 1, { text: "", color: "#000000" }],
1065
+ [3, 1, { text: "", color: "#000000" }],
1066
+ [4, 1, { text: "", color: "#000000" }],
1067
+ [5, 1, { text: "", color: "#000000" }],
1068
+ [6, 1, { text: "", color: "#000000" }],
1069
+ ]), true,
1070
+ "Fingering does not match expected strings"
1071
+ );
1072
+ assert.equal(
1073
+ result?.title,
1074
+ "",
1075
+ "Title should be undefined"
1076
+ );
1077
+ assert.equal(
1078
+ result?.position,
1079
+ undefined,
1080
+ "Position does not match expected undefined"
1081
+ );
1082
+ assert.deepEqual(result?.barres, [], "Barres should be empty");
1083
+ });
1084
+
1085
+ });
1086
+ });