]> Lady’s Gitweb - Etiquette/blob - model.test.js
a648d4f736b8e6fb44dcb06c681c6bf739615a59
[Etiquette] / model.test.js
1 // 📧🏷️ Étiquette ∷ model.test.js
2 // ====================================================================
3 //
4 // Copyright © 2023 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import {
11 assert,
12 assertArrayIncludes,
13 assertEquals,
14 assertFalse,
15 assertObjectMatch,
16 assertStrictEquals,
17 assertThrows,
18 beforeEach,
19 describe,
20 it,
21 } from "./dev-deps.js";
22 import { TagSystem } from "./model.js";
23
24 describe("TagSystem", () => {
25 it("[[Call]] throws", () => {
26 assertThrows(() => {
27 TagSystem();
28 });
29 });
30
31 it("[[Construct]] creates a new TagSystem", () => {
32 assertStrictEquals(
33 Object.getPrototypeOf(new TagSystem("example", "1972-12-31")),
34 TagSystem.prototype,
35 );
36 });
37
38 it("[[Construct]] uses the identifier if provided", () => {
39 assertStrictEquals(
40 new TagSystem("example", "1972-12-31", "etaoin").identifier,
41 "etaoin",
42 );
43 });
44
45 it("[[Construct]] uses an empty identifier if none is provided", () => {
46 assertStrictEquals(
47 new TagSystem("example", "1972-12-31").identifier,
48 "",
49 );
50 });
51
52 it("[[Construct]] throws if provided an invalid domain", () => {
53 assertThrows(() => {
54 new TagSystem("example@example", "1972-12-31");
55 });
56 assertThrows(() => {
57 new TagSystem("0.0.0.0", "1972-12-31");
58 });
59 });
60
61 it("[[Construct]] throws if provided an invalid date", () => {
62 assertThrows(() => {
63 new TagSystem("example", "1969");
64 });
65 assertThrows(() => {
66 new TagSystem("example", "1972-12-31T00:00:00Z");
67 });
68 });
69
70 describe("::Tag", () => {
71 let Tag;
72 let system;
73
74 beforeEach(() => {
75 system = new TagSystem("example", "1972-12-31");
76 Tag = system.Tag;
77 });
78
79 it("[[Get]] returns the same value every time", () => {
80 assertStrictEquals(Tag, system.Tag);
81 });
82
83 it("[[Call]] throws", () => {
84 assertThrows(() => {
85 Tag();
86 });
87 });
88
89 it("[[Construct]] returns a new Tag", () => {
90 assertStrictEquals(
91 Object.getPrototypeOf(new Tag()),
92 Tag.prototype,
93 );
94 });
95
96 it('[[Construct]] defaults the kind to "Tag"', () => {
97 assertStrictEquals(new Tag().kind, "Tag");
98 });
99
100 it("[[Construct]] correctly sets the tag kind", () => {
101 assertStrictEquals(
102 new Tag("RelationshipTag").kind,
103 "RelationshipTag",
104 );
105 });
106
107 it("[[Construct]] defaults the preferred label to the empty string", () => {
108 assertStrictEquals(new Tag().prefLabel, "");
109 });
110
111 it("[[Construct]] correctly sets the preferred label to a simple string", () => {
112 assertStrictEquals(
113 new Tag("RelationshipTag", "Shadow, Me").prefLabel,
114 "Shadow, Me",
115 );
116 });
117
118 it("[[Construct]] initializes tag identifiers to null", () => {
119 assertStrictEquals(
120 new Tag().identifier,
121 null,
122 );
123 });
124
125 it("[[Construct]] correctly sets the preferred label to a language‐tagged string", () => {
126 assertEquals(
127 {
128 ...new Tag("RelationshipTag", {
129 "@value": "Shadow, Me",
130 "@language": "en",
131 }).prefLabel,
132 },
133 { "@value": "Shadow, Me", "@language": "en" },
134 );
135 });
136
137 it("[[Construct]] throws if the tag kind is not recognized", () => {
138 assertThrows(() => {
139 new Tag("NotATag");
140 });
141 });
142
143 describe(".all", () => {
144 it("[[Call]] yields all the persisted tags", () => {
145 const tags = new Set(function* () {
146 let i = 0;
147 while (i++ < 5) {
148 // Generate 5 tags and remember their identifiers.
149 const tag = new Tag();
150 tag.persist();
151 yield tag.identifier;
152 }
153 }());
154 for (const tag of Tag.all()) {
155 assertStrictEquals(
156 Object.getPrototypeOf(tag),
157 Tag.prototype,
158 );
159 }
160 assertEquals(
161 new Set(Array.from(Tag.all(), (tag) => tag.identifier)),
162 tags,
163 );
164 });
165 });
166
167 describe(".fromIRI", () => {
168 it("[[Call]] returns the persisted tag with the given I·R·I", () => {
169 const tag = new Tag();
170 tag.persist();
171 const { identifier, iri } = tag;
172 const retrieved = Tag.fromIRI(iri);
173 assertStrictEquals(
174 Object.getPrototypeOf(retrieved),
175 Tag.prototype,
176 );
177 assertStrictEquals(retrieved.identifier, identifier);
178 });
179
180 it("[[Call]] returns null if no tag with the given I·R·I has been persisted", () => {
181 assertStrictEquals(
182 Tag.fromIRI(
183 `https://${system.authorityName}/tag:${system.taggingEntity}:000-0000`,
184 ),
185 null,
186 );
187 });
188
189 it("[[Call]] returns null if passed an invalid I·R·I", () => {
190 assertStrictEquals(Tag.fromIRI(`bad iri`), null);
191 });
192 });
193
194 describe(".fromIdentifier", () => {
195 it("[[Call]] returns the persisted tag with the given identifier", () => {
196 const tag = new Tag();
197 tag.persist();
198 const { identifier } = tag;
199 const retrieved = Tag.fromIdentifier(identifier);
200 assertStrictEquals(
201 Object.getPrototypeOf(retrieved),
202 Tag.prototype,
203 );
204 assertStrictEquals(retrieved.identifier, identifier);
205 });
206
207 it("[[Call]] returns null if no tag with the given identifier has been persisted", () => {
208 assertStrictEquals(Tag.fromIdentifier("000-0000"), null);
209 });
210
211 it("[[Call]] throws if passed an invalid identifier", () => {
212 assertThrows(() => {
213 Tag.fromIdentifier(""); // wrong format
214 });
215 assertThrows(() => {
216 Tag.fromIdentifier("100-0000"); // bad checksum
217 });
218 });
219 });
220
221 describe(".fromTagURI", () => {
222 it("[[Call]] returns the persisted tag with the given Tag U·R·I", () => {
223 const tag = new Tag();
224 tag.persist();
225 const { identifier, tagURI } = tag;
226 const retrieved = Tag.fromTagURI(tagURI);
227 assertStrictEquals(
228 Object.getPrototypeOf(retrieved),
229 Tag.prototype,
230 );
231 assertStrictEquals(retrieved.identifier, identifier);
232 });
233
234 it("[[Call]] returns null if no tag with the given Tag U·R·I has been persisted", () => {
235 assertStrictEquals(
236 Tag.fromIRI(`tag:${system.taggingEntity}:`),
237 null,
238 );
239 assertStrictEquals(
240 Tag.fromIRI(`tag:${system.taggingEntity}:000-0000`),
241 null,
242 );
243 });
244
245 it("[[Call]] throws if passed an invalid Tag U·R·I", () => {
246 assertThrows(() => {
247 Tag.fromTagURI(""); // wrong format
248 });
249 assertThrows(() => {
250 Tag.fromTagURI(
251 "tag:unexample,1970-01-01:Z", // incorrect tagging entity
252 );
253 });
254 });
255 });
256
257 describe(".getSystem", () => {
258 it("[[Has]] is not present", () => {
259 assertFalse("getSystem" in Tag);
260 });
261 });
262
263 describe(".identifiers", () => {
264 it("[[Call]] yields all the persisted identifiers", () => {
265 const tags = new Set(function* () {
266 let i = 0;
267 while (i++ < 5) {
268 // Generate 5 tags and remember their identifiers.
269 const tag = new Tag();
270 tag.persist();
271 yield tag.identifier;
272 }
273 }());
274 assertEquals(
275 new Set(Tag.identifiers()),
276 tags,
277 );
278 });
279 });
280
281 // `.[Storage.toInstance]` is tested by `.fromIdentifier`.
282
283 describe("::addAltLabel", () => {
284 it("[[Call]] does nothing if called with no arguments", () => {
285 const tag = new Tag();
286 tag.addAltLabel();
287 assertEquals([...tag.altLabels()], []);
288 });
289
290 it("[[Call]] adds the provided alternative labels", () => {
291 const tag = new Tag();
292 tag.addAltLabel(
293 "one",
294 { "@value": "two" },
295 { "@value": "three", "@language": "en" },
296 );
297 assertEquals(
298 Array.from(
299 tag.altLabels(),
300 ($) => typeof $ == "string" ? $ : { ...$ },
301 ),
302 [
303 "one",
304 "two",
305 { "@value": "three", "@language": "en" },
306 ],
307 );
308 });
309 });
310
311 describe("::addBroaderTag", () => {
312 it("[[Call]] does nothing if called with no arguments", () => {
313 const tag = new Tag();
314 tag.addBroaderTag();
315 assertEquals([...tag.broaderTags()], []);
316 });
317
318 it("[[Call]] adds the provided broader tags", () => {
319 const broader = new Tag();
320 broader.persist();
321 const broader2 = new Tag();
322 broader2.persist();
323 const tag = new Tag();
324 tag.addBroaderTag(broader, broader2);
325 assertEquals(
326 Array.from(tag.broaderTags(), ($) => $.identifier),
327 [broader.identifier, broader2.identifier],
328 );
329 });
330
331 it("[[Call]] throws when adding a non‐persisted tag", () => {
332 const tag = new Tag();
333 assertThrows(() => {
334 tag.addBroaderTag(new Tag());
335 });
336 });
337
338 it("[[Call]] throws when adding an unrecognized identifier", () => {
339 const tag = new Tag();
340 assertThrows(() => {
341 tag.addBroaderTag("000-0000"); // not persisted
342 });
343 assertThrows(() => {
344 tag.addBroaderTag(""); // bad format
345 });
346 });
347 });
348
349 describe("::addHiddenLabel", () => {
350 it("[[Call]] does nothing if called with no arguments", () => {
351 const tag = new Tag();
352 tag.addHiddenLabel();
353 assertEquals([...tag.hiddenLabels()], []);
354 });
355
356 it("[[Call]] adds the provided hidden labels", () => {
357 const tag = new Tag();
358 tag.addHiddenLabel(
359 "one",
360 { "@value": "two" },
361 { "@value": "three", "@language": "en" },
362 );
363 assertEquals(
364 Array.from(
365 tag.hiddenLabels(),
366 ($) => typeof $ == "string" ? $ : { ...$ },
367 ),
368 [
369 "one",
370 "two",
371 { "@value": "three", "@language": "en" },
372 ],
373 );
374 });
375 });
376
377 describe("::addInCanonTag", () => {
378 it("[[Call]] does nothing if called with no arguments", () => {
379 const tag = new Tag("EntityTag");
380 tag.addInCanonTag();
381 assertEquals([...tag.inCanonTags()], []);
382 });
383
384 it("[[Call]] adds the provided canon tags", () => {
385 const canon = new Tag("CanonTag");
386 canon.persist();
387 const canon2 = new Tag("CanonTag");
388 canon2.persist();
389 const tag = new Tag("EntityTag");
390 tag.addInCanonTag(canon, canon2);
391 assertEquals(
392 Array.from(tag.inCanonTags(), ($) => $.identifier),
393 [canon.identifier, canon2.identifier],
394 );
395 });
396
397 it("[[Call]] throws when this is not a tag which can be placed in canon", () => {
398 assertThrows(() => {
399 new Tag().addInCanonTag();
400 });
401 });
402
403 it("[[Call]] throws when provided with a non‐canon tag", () => {
404 const notCanon = new Tag();
405 notCanon.persist();
406 const tag = new Tag("EntityTag");
407 assertThrows(() => {
408 tag.addInCanonTag(notCanon);
409 });
410 });
411
412 it("[[Call]] throws when adding a non‐persisted tag", () => {
413 const tag = new Tag("EntityTag");
414 assertThrows(() => {
415 tag.addInCanonTag(new Tag("CanonTag"));
416 });
417 });
418
419 it("[[Call]] throws when adding an unrecognized identifier", () => {
420 const tag = new Tag("EntityTag");
421 assertThrows(() => {
422 tag.addInCanonTag("000-0000"); // not persisted
423 });
424 assertThrows(() => {
425 tag.addInCanonTag(""); // bad format
426 });
427 });
428 });
429
430 describe("::addInvolvesTag", () => {
431 it("[[Call]] does nothing if called with no arguments", () => {
432 const tag = new Tag("ConceptualTag");
433 tag.addInvolvesTag();
434 assertEquals([...tag.involvesTags()], []);
435 });
436
437 it("[[Call]] adds the provided tags", () => {
438 const involved = new Tag();
439 involved.persist();
440 const involved2 = new Tag();
441 involved2.persist();
442 const tag = new Tag("ConceptualTag");
443 tag.addInvolvesTag(involved, involved2);
444 assertEquals(
445 Array.from(tag.involvesTags(), ($) => $.identifier),
446 [involved.identifier, involved2.identifier],
447 );
448 });
449
450 it("[[Call]] throws when this is not a conceptual tag", () => {
451 assertThrows(() => {
452 new Tag().addInvolvesTag();
453 });
454 });
455
456 it("[[Call]] throws when this is a relationship tag and provided with a non‐involvable tag", () => {
457 const notInvolved = new Tag();
458 notInvolved.persist();
459 const tag = new Tag("RelationshipTag");
460 assertThrows(() => {
461 tag.addInvolvesTag(notInvolved);
462 });
463 });
464
465 it("[[Call]] throws when adding a non‐persisted tag", () => {
466 const tag = new Tag("ConceptualTag");
467 assertThrows(() => {
468 tag.addInvolvesTag(new Tag());
469 });
470 });
471
472 it("[[Call]] throws when adding an unrecognized identifier", () => {
473 const tag = new Tag("ConceptualTag");
474 assertThrows(() => {
475 tag.addInvolvesTag("000-0000"); // not persisted
476 });
477 assertThrows(() => {
478 tag.addInvolvesTag(""); // bad format
479 });
480 });
481 });
482
483 // `::altLabels` is tested by `::addAltLabel`.
484
485 describe("::authorityName", () => {
486 it("[[Get]] returns the authority name of the tag system", () => {
487 assertStrictEquals(
488 new Tag().authorityName,
489 system.authorityName,
490 );
491 });
492 });
493
494 // `::broaderTags` is tested by `::addBroaderTag`.
495
496 describe("::broaderTransitiveTags", () => {
497 it("[[Call]] returns broader tags transitively", () => {
498 const superBroad = new Tag();
499 superBroad.persist();
500 const broad = new Tag();
501 broad.addBroaderTag(superBroad);
502 broad.persist();
503 const tag = new Tag();
504 tag.addBroaderTag(broad);
505 assertEquals(
506 Array.from(tag.broaderTransitiveTags(), ($) => $.identifier),
507 [broad.identifier, superBroad.identifier],
508 );
509 });
510
511 it("[[Call]] cannot recurse infinitely", () => {
512 const tag = new Tag();
513 tag.persist();
514 const broad = new Tag();
515 broad.addBroaderTag(tag);
516 broad.persist();
517 tag.addBroaderTag(broad);
518 tag.persist();
519 assertEquals(
520 Array.from(tag.broaderTransitiveTags(), ($) => $.identifier),
521 [broad.identifier, tag.identifier],
522 );
523 });
524 });
525
526 describe("::deleteAltLabel", () => {
527 it("[[Call]] does nothing if called with no arguments", () => {
528 const tag = new Tag();
529 tag.addAltLabel("etaoin");
530 tag.deleteAltLabel();
531 assertEquals([...tag.altLabels()], ["etaoin"]);
532 });
533
534 it("[[Call]] deletes only the provided hidden labels", () => {
535 const tag = new Tag();
536 tag.addAltLabel(
537 "one",
538 "two",
539 { "@value": "three", "@language": "en" },
540 "four",
541 );
542 tag.deleteAltLabel(
543 "one",
544 { "@value": "two" },
545 { "@value": "three", "@language": "en" },
546 { "@value": "four", "@language": "en" },
547 );
548 assertEquals([...tag.altLabels()], ["four"]);
549 });
550 });
551
552 describe("::deleteBroaderTag", () => {
553 it("[[Call]] does nothing if called with no arguments", () => {
554 const broader = new Tag();
555 broader.persist();
556 const tag = new Tag();
557 tag.addBroaderTag(broader);
558 tag.deleteBroaderTag();
559 assertEquals(
560 Array.from(tag.broaderTags(), ($) => $.identifier),
561 [broader.identifier],
562 );
563 });
564
565 it("[[Call]] deletes only the provided broader tags", () => {
566 const superBroader = new Tag();
567 superBroader.persist();
568 const broader = new Tag();
569 broader.addBroaderTag(superBroader);
570 broader.persist();
571 const broader2 = new Tag();
572 broader2.addBroaderTag(superBroader);
573 broader2.persist();
574 const tag = new Tag();
575 tag.addBroaderTag(broader, broader2);
576 tag.deleteBroaderTag(broader, superBroader, "000-0000", "");
577 assertEquals(
578 Array.from(tag.broaderTags(), ($) => $.identifier),
579 [broader2.identifier],
580 );
581 });
582 });
583
584 describe("::deleteHiddenLabel", () => {
585 it("[[Call]] does nothing if called with no arguments", () => {
586 const tag = new Tag();
587 tag.addHiddenLabel("etaoin");
588 tag.deleteHiddenLabel();
589 assertEquals([...tag.hiddenLabels()], ["etaoin"]);
590 });
591
592 it("[[Call]] deletes only the provided alternative labels", () => {
593 const tag = new Tag();
594 tag.addHiddenLabel(
595 "one",
596 "two",
597 { "@value": "three", "@language": "en" },
598 "four",
599 );
600 tag.deleteHiddenLabel(
601 "one",
602 { "@value": "two" },
603 { "@value": "three", "@language": "en" },
604 { "@value": "four", "@language": "en" },
605 );
606 assertEquals([...tag.hiddenLabels()], ["four"]);
607 });
608 });
609
610 describe("::deleteInCanonTag", () => {
611 it("[[Call]] does nothing if called with no arguments", () => {
612 const canon = new Tag("CanonTag");
613 canon.persist();
614 const tag = new Tag("EntityTag");
615 tag.addInCanonTag(canon);
616 tag.deleteInCanonTag();
617 assertEquals(
618 Array.from(tag.inCanonTags(), ($) => $.identifier),
619 [canon.identifier],
620 );
621 });
622
623 it("[[Call]] deletes only the provided canon tags", () => {
624 const canon = new Tag("CanonTag");
625 canon.persist();
626 const canon2 = new Tag("CanonTag");
627 canon2.persist();
628 const tag = new Tag("EntityTag");
629 tag.addInCanonTag(canon, canon2);
630 tag.deleteInCanonTag(canon, "000-0000", "");
631 assertEquals(
632 Array.from(tag.inCanonTags(), ($) => $.identifier),
633 [canon2.identifier],
634 );
635 });
636 });
637
638 describe("::deleteInvolvesTag", () => {
639 it("[[Call]] does nothing if called with no arguments", () => {
640 const involved = new Tag();
641 involved.persist();
642 const tag = new Tag("ConceptualTag");
643 tag.addInvolvesTag(involved);
644 tag.deleteInvolvesTag();
645 assertEquals(
646 Array.from(tag.involvesTags(), ($) => $.identifier),
647 [involved.identifier],
648 );
649 });
650
651 it("[[Call]] deletes only the provided involved tags", () => {
652 const character = new Tag("CharacterTag");
653 character.persist();
654 const involved = new Tag("RelationshipTag");
655 involved.addInvolvesTag(character);
656 involved.persist();
657 const involved2 = new Tag("RelationshipTag");
658 involved2.addInvolvesTag(character);
659 involved2.persist();
660 const tag = new Tag("RelationshipTag");
661 tag.addInvolvesTag(involved, involved2);
662 tag.deleteInvolvesTag(involved, character, "000-0000", "");
663 assertEquals(
664 Array.from(tag.involvesTags(), ($) => $.identifier),
665 [involved2.identifier],
666 );
667 });
668 });
669
670 describe("::hasInCanonTags", () => {
671 it("[[Call]] yields the persisted tags which have this tag in canon", () => {
672 const canon = new Tag("CanonTag");
673 canon.persist();
674 const entity = new Tag("EntityTag");
675 entity.addInCanonTag(canon);
676 entity.persist();
677 const entity2 = new Tag("EntityTag");
678 entity2.addInCanonTag(canon);
679 entity2.persist();
680 const tag = Tag.fromIdentifier(canon.identifier); // reload
681 assertEquals(
682 Array.from(tag.hasInCanonTags(), ($) => $.identifier),
683 [entity.identifier, entity2.identifier],
684 );
685 });
686 });
687
688 // `::hiddenLabels` is tested by `::addHiddenLabel`.
689
690 // `::identifier` is tested by a `.fromIdentifier`.
691
692 // `::inCanonTags` is tested by `::addInCanonTag`.
693
694 describe("::involvedInTags", () => {
695 it("[[Call]] yields the persisted tags which involve this tag", () => {
696 const involved = new Tag();
697 involved.persist();
698 const conceptual = new Tag("ConceptualTag");
699 conceptual.addInvolvesTag(involved);
700 conceptual.persist();
701 const conceptual2 = new Tag("ConceptualTag");
702 conceptual2.addInvolvesTag(involved);
703 conceptual2.persist();
704 const tag = Tag.fromIdentifier(involved.identifier); // reload
705 assertEquals(
706 Array.from(tag.involvedInTags(), ($) => $.identifier),
707 [conceptual.identifier, conceptual2.identifier],
708 );
709 });
710 });
711
712 // `::involvesTags` is tested by `::addInvolvesTag`.
713
714 // `::iri` is tested by a `.fromIRI`.
715
716 // `::kind` is tested by the constructor.
717
718 describe("::narrowerTags", () => {
719 it("[[Call]] yields the persisted tags which are narrower than this tag", () => {
720 const broader = new Tag();
721 broader.persist();
722 const narrower = new Tag();
723 narrower.addBroaderTag(broader);
724 narrower.persist();
725 const narrower2 = new Tag();
726 narrower2.addBroaderTag(broader);
727 narrower2.persist();
728 const tag = Tag.fromIdentifier(broader.identifier); // reload
729 assertEquals(
730 Array.from(tag.narrowerTags(), ($) => $.identifier),
731 [narrower.identifier, narrower2.identifier],
732 );
733 });
734 });
735
736 describe("::narrowerTransitiveTags", () => {
737 it("[[Call]] returns narrower tags transitively", () => {
738 const broad = new Tag();
739 broad.persist();
740 const narrow = new Tag();
741 narrow.addBroaderTag(broad);
742 narrow.persist();
743 const superNarrow = new Tag();
744 superNarrow.addBroaderTag(narrow);
745 superNarrow.persist();
746 const tag = Tag.fromIdentifier(broad.identifier); // reload
747 assertEquals(
748 Array.from(
749 tag.narrowerTransitiveTags(),
750 ($) => $.identifier,
751 ),
752 [narrow.identifier, superNarrow.identifier],
753 );
754 });
755
756 it("[[Call]] cannot recurse infinitely", () => {
757 const tag = new Tag();
758 tag.persist();
759 const broad = new Tag();
760 broad.addBroaderTag(tag);
761 broad.persist();
762 tag.addBroaderTag(broad);
763 tag.persist();
764 assertEquals(
765 Array.from(tag.broaderTransitiveTags(), ($) => $.identifier),
766 [broad.identifier, tag.identifier],
767 );
768 });
769 });
770
771 describe("::persist", () => {
772 it("[[Call]] returns an object with expected properties if there were changes", () => {
773 const tag = new Tag();
774 const activity = tag.persist();
775 assertObjectMatch(
776 activity,
777 {
778 "@context":
779 "https://ns.1024.gdn/Tagging/discovery.context.jsonld",
780 context: system.iri,
781 object: tag.iri,
782 },
783 );
784 assertArrayIncludes(activity["@type"], ["TagActivity"]);
785 assert("endTime" in activity);
786 });
787
788 it("[[Call]] returns a Create activity with a type predicate for new objects", () => {
789 const activity = new Tag().persist();
790 assertEquals(activity["@type"], ["TagActivity", "Create"]);
791 assertArrayIncludes(activity.states, [{
792 predicate: "a",
793 object: "Tag",
794 }]);
795 });
796
797 it("[[Call]] returns an Update activity for old objects", () => {
798 const tag = new Tag();
799 tag.persist();
800 tag.prefLabel = "etaoin";
801 const activity = tag.persist();
802 assertEquals(activity["@type"], ["TagActivity", "Update"]);
803 });
804
805 it("[[Call]] states and unstates changes", () => {
806 const broader1 = new Tag();
807 broader1.persist();
808 const broader2 = new Tag();
809 broader2.persist();
810 const tag = new Tag();
811 tag.addBroaderTag(broader1);
812 tag.persist();
813 tag.prefLabel = "etaoin";
814 tag.deleteBroaderTag(broader1);
815 tag.addBroaderTag(broader2);
816 const activity = tag.persist();
817 assertObjectMatch(activity, {
818 unstates: [
819 { predicate: "prefLabel", object: { "@value": "" } },
820 { predicate: "broader", object: broader1.iri },
821 ],
822 states: [
823 { predicate: "prefLabel", object: { "@value": "etaoin" } },
824 { predicate: "broader", object: broader2.iri },
825 ],
826 });
827 });
828
829 it("[[Call]] doesn’t state if there are no additions", () => {
830 const tag = new Tag();
831 tag.addAltLabel("etaoin");
832 tag.persist();
833 tag.deleteAltLabel("etaoin");
834 const activity = tag.persist();
835 assertFalse("state" in activity);
836 });
837
838 it("[[Call]] doesn’t unstate if there are no removals", () => {
839 const tag = new Tag();
840 tag.persist();
841 tag.addAltLabel("etaoin");
842 const activity = tag.persist();
843 assertFalse("unstate" in activity);
844 });
845
846 it("[[Call]] returns null if no meaningful changes were made", () => {
847 const tag = new Tag();
848 tag.persist();
849 const activity = tag.persist();
850 assertStrictEquals(activity, null);
851 });
852
853 it("[[Call]] returns undefined for a silent persist", () => {
854 const broader = new Tag();
855 broader.persist();
856 const tag = new Tag();
857 tag.prefLabel = "etaoin";
858 tag.addBroaderTag(broader);
859 assertStrictEquals(tag.persist(true), undefined);
860 });
861 });
862
863 describe("::prefLabel", () => {
864 it("[[Set]] sets the preferred label", () => {
865 const tag = new Tag();
866 tag.prefLabel = "one";
867 assertStrictEquals(tag.prefLabel, "one");
868 tag.prefLabel = { "@value": "two" };
869 assertStrictEquals(tag.prefLabel, "two");
870 tag.prefLabel = { "@value": "three", "@language": "en" };
871 assertEquals(
872 { ...tag.prefLabel },
873 { "@value": "three", "@language": "en" },
874 );
875 });
876 });
877
878 // `::tagURI` is tested by a `.fromTagURI`.
879
880 describe("::taggingEntity", () => {
881 it("[[Get]] returns the tagging entity of the tag system", () => {
882 assertStrictEquals(
883 new Tag().taggingEntity,
884 system.taggingEntity,
885 );
886 });
887 });
888
889 describe("::toString", () => {
890 it("[[Get]] returns the string value of the preferred label", () => {
891 const tag = new Tag();
892 tag.prefLabel = { "@value": "etaoin", "@language": "zxx" };
893 assertStrictEquals(tag.toString(), "etaoin");
894 });
895 });
896
897 // `::[Storage.toObject]` is tested by `::persist`.
898 });
899
900 describe("::authorityName", () => {
901 it("[[Get]] returns the authority name", () => {
902 const system = new TagSystem("etaoin.example", "1972-12-31");
903 assertStrictEquals(system.authorityName, "etaoin.example");
904 });
905 });
906
907 describe("::authorityName", () => {
908 it("[[Get]] returns the date", () => {
909 const system = new TagSystem("etaoin.example", "1972-12-31");
910 assertStrictEquals(system.date, "1972-12-31");
911 });
912 });
913
914 describe("::identifiers", () => {
915 it("[[Get]] yields the extant entities", () => {
916 const system = new TagSystem("etaoin.example", "1972-12-31");
917 const tags = new Set(function* () {
918 let i = 0;
919 while (i++ < 5) {
920 // Generate 5 tags and remember their identifiers.
921 const tag = new system.Tag();
922 tag.persist();
923 yield tag.identifier;
924 }
925 }());
926 assertEquals(
927 new Set(Array.from(system.entities(), ($) => $.identifier)),
928 tags,
929 );
930 });
931 });
932
933 describe("::identifier", () => {
934 it("[[Get]] returns the identifier", () => {
935 const system = new TagSystem("etaoin.example", "1972-12-31");
936 assertStrictEquals(system.identifier, "");
937 const system2 = new TagSystem(
938 "etaoin.example",
939 "1972-12-31",
940 "etaoin",
941 );
942 assertStrictEquals(system2.identifier, "etaoin");
943 });
944 });
945
946 describe("::identifiers", () => {
947 it("[[Get]] yields the identifiers in use", () => {
948 const system = new TagSystem("etaoin.example", "1972-12-31");
949 const tags = new Set(function* () {
950 let i = 0;
951 while (i++ < 5) {
952 // Generate 5 tags and remember their identifiers.
953 const tag = new system.Tag();
954 tag.persist();
955 yield tag.identifier;
956 }
957 }());
958 assertEquals(new Set(system.identifiers()), tags);
959 });
960 });
961
962 describe("::iri", () => {
963 it("[[Get]] returns the I·R·I", () => {
964 const system = new TagSystem("etaoin.example", "1972-12-31");
965 assertStrictEquals(
966 system.iri,
967 "https://etaoin.example/tag:etaoin.example,1972-12-31:",
968 );
969 const system2 = new TagSystem(
970 "etaoin.example",
971 "1972-12-31",
972 "etaoin",
973 );
974 assertStrictEquals(
975 system2.iri,
976 "https://etaoin.example/tag:etaoin.example,1972-12-31:etaoin",
977 );
978 });
979 });
980
981 describe("::tagURI", () => {
982 it("[[Get]] returns the Tag U·R·I", () => {
983 const system = new TagSystem("etaoin.example", "1972-12-31");
984 assertStrictEquals(
985 system.tagURI,
986 "tag:etaoin.example,1972-12-31:",
987 );
988 const system2 = new TagSystem(
989 "etaoin.example",
990 "1972-12-31",
991 "etaoin",
992 );
993 assertStrictEquals(
994 system2.tagURI,
995 "tag:etaoin.example,1972-12-31:etaoin",
996 );
997 });
998 });
999
1000 describe("::taggingEntity", () => {
1001 it("[[Get]] returns the tagging entity", () => {
1002 const system = new TagSystem("etaoin.example", "1972-12-31");
1003 assertStrictEquals(
1004 system.taggingEntity,
1005 "etaoin.example,1972-12-31",
1006 );
1007 });
1008 });
1009 });
This page took 0.162711 seconds and 3 git commands to generate.