com.wallstop-studios.dxmessaging 2.1.5 → 2.1.6

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 (161) hide show
  1. package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.AssemblyInfo.cs +1 -1
  2. package/.cspell.json +4 -1
  3. package/.github/workflows/actionlint.yml +11 -1
  4. package/.github/workflows/csharpier-autofix.yml +34 -3
  5. package/.github/workflows/dotnet-tests.yml +13 -0
  6. package/.github/workflows/format-on-demand.yml +38 -44
  7. package/.github/workflows/json-format-check.yml +24 -0
  8. package/.github/workflows/lint-doc-links.yml +13 -0
  9. package/.github/workflows/markdown-json.yml +21 -4
  10. package/.github/workflows/markdown-link-text-check.yml +10 -0
  11. package/.github/workflows/markdown-link-validity.yml +10 -0
  12. package/.github/workflows/markdownlint.yml +7 -5
  13. package/.github/workflows/prettier-autofix.yml +67 -11
  14. package/.github/workflows/release-drafter.yml +2 -2
  15. package/.github/workflows/sync-wiki.yml +3 -3
  16. package/.github/workflows/yaml-format-lint.yml +26 -0
  17. package/.llm/context.md +113 -3
  18. package/.llm/skills/documentation/changelog-management.md +38 -0
  19. package/.llm/skills/documentation/documentation-style-guide.md +18 -0
  20. package/.llm/skills/documentation/documentation-update-workflow.md +2 -0
  21. package/.llm/skills/documentation/documentation-updates.md +2 -0
  22. package/.llm/skills/documentation/markdown-compatibility.md +476 -0
  23. package/.llm/skills/documentation/mermaid-theming.md +326 -0
  24. package/.llm/skills/documentation/mkdocs-navigation.md +290 -0
  25. package/.llm/skills/github-actions/git-renormalize-patterns.md +231 -0
  26. package/.llm/skills/github-actions/workflow-consistency.md +346 -0
  27. package/.llm/skills/index.md +53 -27
  28. package/.llm/skills/scripting/javascript-code-quality.md +417 -0
  29. package/.llm/skills/scripting/regex-documentation.md +461 -0
  30. package/.llm/skills/scripting/shell-best-practices.md +55 -4
  31. package/.llm/skills/scripting/validation-patterns.md +418 -0
  32. package/.llm/skills/specification.md +4 -1
  33. package/.llm/skills/testing/test-code-quality.md +243 -0
  34. package/.llm/skills/testing/test-production-code.md +348 -0
  35. package/CHANGELOG.md +11 -0
  36. package/README.md +0 -11
  37. package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef +1 -6
  38. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef +1 -1
  39. package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef +1 -1
  40. package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef +1 -1
  41. package/coverage/clover.xml +216 -3
  42. package/coverage/clover.xml.meta +7 -7
  43. package/coverage/coverage-final.json +2 -1
  44. package/coverage/coverage-final.json.meta +7 -7
  45. package/coverage/lcov-report/base.css.meta +1 -1
  46. package/coverage/lcov-report/block-navigation.js.meta +1 -1
  47. package/coverage/lcov-report/favicon.png.meta +1 -1
  48. package/coverage/lcov-report/index.html +25 -10
  49. package/coverage/lcov-report/index.html.meta +7 -7
  50. package/coverage/lcov-report/prettify.css.meta +1 -1
  51. package/coverage/lcov-report/prettify.js.meta +1 -1
  52. package/coverage/lcov-report/sort-arrow-sprite.png.meta +1 -1
  53. package/coverage/lcov-report/sorter.js.meta +1 -1
  54. package/coverage/lcov-report/transform-docs-to-wiki.js.html +1 -1
  55. package/coverage/lcov-report/transform-docs-to-wiki.js.html.meta +7 -7
  56. package/coverage/lcov-report/vendor.meta +1 -1
  57. package/coverage/lcov-report.meta +8 -8
  58. package/coverage/lcov.info +365 -0
  59. package/coverage/lcov.info.meta +7 -7
  60. package/docs/architecture/design-and-architecture.md +0 -1
  61. package/docs/concepts/index.md +37 -0
  62. package/docs/concepts/index.md.meta +7 -0
  63. package/docs/concepts/interceptors-and-ordering.md +0 -2
  64. package/docs/concepts/mental-model.md +390 -0
  65. package/docs/concepts/mental-model.md.meta +7 -0
  66. package/docs/concepts/message-types.md +0 -1
  67. package/docs/getting-started/getting-started.md +1 -0
  68. package/docs/getting-started/index.md +6 -5
  69. package/docs/getting-started/overview.md +1 -0
  70. package/docs/getting-started/quick-start.md +2 -1
  71. package/docs/getting-started/visual-guide.md +4 -10
  72. package/docs/hooks.py +10 -1
  73. package/docs/images/DxMessaging-banner.svg +1 -1
  74. package/docs/index.md +7 -7
  75. package/docs/javascripts/mermaid-config.js +44 -4
  76. package/docs/reference/helpers.md +130 -154
  77. package/docs/reference/quick-reference.md +5 -1
  78. package/docs/reference/reference.md +124 -130
  79. package/mkdocs.yml +2 -0
  80. package/package.json +1 -1
  81. package/scripts/__tests__/generate-skills-index.test.js +397 -0
  82. package/scripts/__tests__/generate-skills-index.test.js.meta +7 -0
  83. package/scripts/__tests__/mermaid-config.test.js +467 -0
  84. package/scripts/__tests__/mermaid-config.test.js.meta +7 -0
  85. package/scripts/__tests__/validate-skills-optional-fields.test.js +1474 -0
  86. package/scripts/__tests__/validate-skills-optional-fields.test.js.meta +7 -0
  87. package/scripts/__tests__/validate-skills-required-fields.test.js +188 -0
  88. package/scripts/__tests__/validate-skills-required-fields.test.js.meta +7 -0
  89. package/scripts/__tests__/validate-skills-tags.test.js +353 -0
  90. package/scripts/__tests__/validate-skills-tags.test.js.meta +7 -0
  91. package/scripts/__tests__/validate-workflows.test.js +188 -0
  92. package/scripts/__tests__/validate-workflows.test.js.meta +7 -0
  93. package/scripts/generate-skills-index.js +88 -3
  94. package/scripts/validate-skills.js +230 -30
  95. package/scripts/validate-workflows.js +272 -0
  96. package/scripts/validate-workflows.js.meta +7 -0
  97. package/site/404.html +1 -1
  98. package/site/advanced/emit-shorthands/index.html +2 -2
  99. package/site/advanced/message-bus-providers/index.html +2 -2
  100. package/site/advanced/registration-builders/index.html +2 -2
  101. package/site/advanced/runtime-configuration/index.html +2 -2
  102. package/site/advanced/string-messages/index.html +2 -2
  103. package/site/advanced.meta +1 -1
  104. package/site/architecture/comparisons/index.html +2 -2
  105. package/site/architecture/design-and-architecture/index.html +2 -2
  106. package/site/architecture/performance/index.html +1 -1
  107. package/site/architecture.meta +1 -1
  108. package/site/concepts/index.html +1 -0
  109. package/site/concepts/index.html.meta +7 -0
  110. package/site/concepts/interceptors-and-ordering/index.html +4 -4
  111. package/site/concepts/listening-patterns/index.html +2 -2
  112. package/site/concepts/mental-model/index.html +146 -0
  113. package/site/concepts/mental-model/index.html.meta +7 -0
  114. package/site/concepts/mental-model.meta +8 -0
  115. package/site/concepts/message-types/index.html +2 -2
  116. package/site/concepts/targeting-and-context/index.html +2 -2
  117. package/site/concepts.meta +1 -1
  118. package/site/examples/end-to-end/index.html +2 -2
  119. package/site/examples/end-to-end-scene-transitions/index.html +2 -2
  120. package/site/examples.meta +1 -1
  121. package/site/getting-started/getting-started/index.html +3 -3
  122. package/site/getting-started/index.html +4 -4
  123. package/site/getting-started/install/index.html +3 -3
  124. package/site/getting-started/overview/index.html +2 -2
  125. package/site/getting-started/quick-start/index.html +2 -2
  126. package/site/getting-started/visual-guide/index.html +11 -11
  127. package/site/getting-started.meta +1 -1
  128. package/site/guides/advanced/index.html +2 -2
  129. package/site/guides/diagnostics/index.html +2 -2
  130. package/site/guides/migration-guide/index.html +2 -2
  131. package/site/guides/patterns/index.html +2 -2
  132. package/site/guides/testing/index.html +2 -2
  133. package/site/guides/unity-integration/index.html +2 -2
  134. package/site/guides.meta +1 -1
  135. package/site/hooks.py.meta +1 -1
  136. package/site/images/DxMessaging-banner.svg +119 -0
  137. package/site/images/DxMessaging-banner.svg.meta +7 -0
  138. package/site/images.meta +8 -0
  139. package/site/index.html +2 -2
  140. package/site/integrations/index.html +2 -2
  141. package/site/integrations/reflex/index.html +2 -2
  142. package/site/integrations/vcontainer/index.html +2 -2
  143. package/site/integrations/zenject/index.html +2 -2
  144. package/site/integrations.meta +1 -1
  145. package/site/javascripts/csharp-highlight.js.meta +7 -7
  146. package/site/javascripts/mermaid-config.js +4 -1
  147. package/site/javascripts/mermaid-config.js.meta +1 -1
  148. package/site/javascripts.meta +1 -1
  149. package/site/reference/compatibility/index.html +1 -1
  150. package/site/reference/faq/index.html +1 -1
  151. package/site/reference/glossary/index.html +2 -2
  152. package/site/reference/helpers/index.html +15 -15
  153. package/site/reference/quick-reference/index.html +3 -3
  154. package/site/reference/reference/index.html +37 -37
  155. package/site/reference/troubleshooting/index.html +1 -1
  156. package/site/reference.meta +1 -1
  157. package/site/search/search_index.json +1 -1
  158. package/site/sitemap.xml +46 -38
  159. package/site/sitemap.xml.gz +0 -0
  160. package/site/stylesheets/extra.css.meta +1 -1
  161. package/site/stylesheets.meta +1 -1
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: eda9624434fd28f4e94513c20aea9342
3
+ DefaultImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
@@ -0,0 +1,188 @@
1
+ /**
2
+ * @fileoverview Tests for validate-skills.js required field validation logic.
3
+ *
4
+ * These tests validate the required field detection in skill files:
5
+ * - Missing required fields (undefined/null)
6
+ * - Empty required fields (empty string)
7
+ * - Valid required fields
8
+ *
9
+ * The validation logic ensures that skill files have all required frontmatter
10
+ * fields properly populated.
11
+ */
12
+
13
+ "use strict";
14
+
15
+ const {
16
+ validateRequiredField,
17
+ validateRequiredFields,
18
+ REQUIRED_FIELDS,
19
+ } = require('../validate-skills.js');
20
+
21
+ describe("validate-skills required field validation", () => {
22
+ const testPath = "testing/sample-skill.md";
23
+
24
+ describe("missing required fields (undefined/null)", () => {
25
+ test("should error when required field is undefined", () => {
26
+ const frontmatter = {
27
+ id: "sample-skill",
28
+ category: "testing",
29
+ version: "1.0.0",
30
+ created: "2025-01-01",
31
+ updated: "2025-01-01",
32
+ status: "active",
33
+ // title is undefined
34
+ };
35
+
36
+ const errors = validateRequiredField(frontmatter, 'title', testPath);
37
+
38
+ expect(errors).toHaveLength(1);
39
+ expect(errors[0].field).toBe("title");
40
+ expect(errors[0].message).toContain("Required field 'title' is missing");
41
+ });
42
+
43
+ test("should error when required field is null", () => {
44
+ const frontmatter = {
45
+ title: null,
46
+ id: "sample-skill",
47
+ category: "testing",
48
+ version: "1.0.0",
49
+ created: "2025-01-01",
50
+ updated: "2025-01-01",
51
+ status: "active",
52
+ };
53
+
54
+ const errors = validateRequiredField(frontmatter, 'title', testPath);
55
+
56
+ expect(errors).toHaveLength(1);
57
+ expect(errors[0].field).toBe("title");
58
+ expect(errors[0].message).toContain("Required field 'title' is missing");
59
+ });
60
+ });
61
+
62
+ describe("empty required fields", () => {
63
+ test("should error when required field is empty string", () => {
64
+ const frontmatter = {
65
+ title: "",
66
+ id: "sample-skill",
67
+ category: "testing",
68
+ version: "1.0.0",
69
+ created: "2025-01-01",
70
+ updated: "2025-01-01",
71
+ status: "active",
72
+ };
73
+
74
+ const errors = validateRequiredField(frontmatter, 'title', testPath);
75
+
76
+ expect(errors).toHaveLength(1);
77
+ expect(errors[0].field).toBe("title");
78
+ expect(errors[0].message).toContain("Required field 'title' is empty");
79
+ });
80
+
81
+ test("should distinguish between missing and empty in error messages", () => {
82
+ const missingFrontmatter = {
83
+ id: "sample-skill",
84
+ // title is undefined
85
+ };
86
+
87
+ const emptyFrontmatter = {
88
+ title: "",
89
+ id: "sample-skill",
90
+ };
91
+
92
+ const missingErrors = validateRequiredField(missingFrontmatter, 'title', testPath);
93
+ const emptyErrors = validateRequiredField(emptyFrontmatter, 'title', testPath);
94
+
95
+ expect(missingErrors[0].message).toContain("is missing");
96
+ expect(emptyErrors[0].message).toContain("is empty");
97
+ expect(missingErrors[0].message).not.toEqual(emptyErrors[0].message);
98
+ });
99
+ });
100
+
101
+ describe("valid required fields", () => {
102
+ test("should not error when required field has valid string value", () => {
103
+ const frontmatter = {
104
+ title: "Sample Skill",
105
+ id: "sample-skill",
106
+ category: "testing",
107
+ version: "1.0.0",
108
+ created: "2025-01-01",
109
+ updated: "2025-01-01",
110
+ status: "active",
111
+ };
112
+
113
+ const errors = validateRequiredField(frontmatter, 'title', testPath);
114
+
115
+ expect(errors).toHaveLength(0);
116
+ });
117
+
118
+ test("should not error when all required fields are present", () => {
119
+ const frontmatter = {
120
+ title: "Sample Skill",
121
+ id: "sample-skill",
122
+ category: "testing",
123
+ version: "1.0.0",
124
+ created: "2025-01-01",
125
+ updated: "2025-01-01",
126
+ status: "active",
127
+ };
128
+
129
+ const errors = validateRequiredFields(frontmatter, testPath);
130
+
131
+ expect(errors).toHaveLength(0);
132
+ });
133
+ });
134
+
135
+ describe("multiple missing fields", () => {
136
+ test("should report errors for all missing required fields", () => {
137
+ const frontmatter = {
138
+ title: "Sample Skill",
139
+ // id is undefined
140
+ category: "testing",
141
+ // version is undefined
142
+ created: "2025-01-01",
143
+ updated: "2025-01-01",
144
+ status: "active",
145
+ };
146
+
147
+ const errors = validateRequiredFields(frontmatter, testPath);
148
+
149
+ expect(errors).toHaveLength(2);
150
+ expect(errors.map(e => e.field)).toContain("id");
151
+ expect(errors.map(e => e.field)).toContain("version");
152
+ });
153
+
154
+ test("should report errors for mix of missing and empty fields", () => {
155
+ const frontmatter = {
156
+ title: "Sample Skill",
157
+ id: null, // missing (null)
158
+ category: "", // empty
159
+ version: "1.0.0",
160
+ created: "2025-01-01",
161
+ updated: "2025-01-01",
162
+ status: "active",
163
+ };
164
+
165
+ const errors = validateRequiredFields(frontmatter, testPath);
166
+
167
+ expect(errors).toHaveLength(2);
168
+
169
+ const idError = errors.find(e => e.field === "id");
170
+ const categoryError = errors.find(e => e.field === "category");
171
+
172
+ expect(idError.message).toContain("is missing");
173
+ expect(categoryError.message).toContain("is empty");
174
+ });
175
+ });
176
+
177
+ describe("each required field", () => {
178
+ test.each(REQUIRED_FIELDS)("should validate '%s' as required field", (field) => {
179
+ const frontmatter = {};
180
+
181
+ const errors = validateRequiredField(frontmatter, field, testPath);
182
+
183
+ expect(errors).toHaveLength(1);
184
+ expect(errors[0].field).toBe(field);
185
+ expect(errors[0].message).toContain(`Required field '${field}' is missing`);
186
+ });
187
+ });
188
+ });
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: 44d2c7652f77a384fad8119aa2ca0ce3
3
+ DefaultImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
@@ -0,0 +1,353 @@
1
+ /**
2
+ * @fileoverview Tests for validate-skills.js tags validation logic.
3
+ *
4
+ * These tests validate the tags field validation in skill files:
5
+ * - Missing tags (undefined/null)
6
+ * - Wrong type (string, object, number instead of array)
7
+ * - Empty tags array
8
+ * - Valid tags array
9
+ *
10
+ * The validation logic ensures that skill files have properly formatted
11
+ * tags arrays for the skills index generation.
12
+ */
13
+
14
+ "use strict";
15
+
16
+ const { validateTags } = require('../validate-skills.js');
17
+
18
+ describe("validate-skills tags validation", () => {
19
+ const testPath = "testing/sample-skill.md";
20
+
21
+ describe("missing tags field", () => {
22
+ test("should warn when tags is undefined", () => {
23
+ const frontmatter = {
24
+ title: "Sample Skill",
25
+ id: "sample-skill",
26
+ // tags is undefined
27
+ };
28
+
29
+ const warnings = validateTags(frontmatter, testPath);
30
+
31
+ expect(warnings).toHaveLength(1);
32
+ expect(warnings[0].field).toBe("tags");
33
+ expect(warnings[0].message).toContain("Missing 'tags' array");
34
+ });
35
+
36
+ test("should warn when tags is null", () => {
37
+ const frontmatter = {
38
+ title: "Sample Skill",
39
+ id: "sample-skill",
40
+ tags: null,
41
+ };
42
+
43
+ const warnings = validateTags(frontmatter, testPath);
44
+
45
+ expect(warnings).toHaveLength(1);
46
+ expect(warnings[0].field).toBe("tags");
47
+ expect(warnings[0].message).toContain("Missing 'tags' array");
48
+ });
49
+ });
50
+
51
+ describe("wrong type for tags", () => {
52
+ test("should warn when tags is an empty string", () => {
53
+ const frontmatter = {
54
+ title: "Sample Skill",
55
+ id: "sample-skill",
56
+ tags: "",
57
+ };
58
+
59
+ const warnings = validateTags(frontmatter, testPath);
60
+
61
+ expect(warnings).toHaveLength(1);
62
+ expect(warnings[0].field).toBe("tags");
63
+ // Empty string is defined but wrong type
64
+ expect(warnings[0].message).toContain("'tags' must be an array");
65
+ expect(warnings[0].message).toContain("got string");
66
+ });
67
+
68
+ test("should warn when tags is a zero", () => {
69
+ const frontmatter = {
70
+ title: "Sample Skill",
71
+ id: "sample-skill",
72
+ tags: 0,
73
+ };
74
+
75
+ const warnings = validateTags(frontmatter, testPath);
76
+
77
+ expect(warnings).toHaveLength(1);
78
+ expect(warnings[0].field).toBe("tags");
79
+ // 0 is defined but wrong type
80
+ expect(warnings[0].message).toContain("'tags' must be an array");
81
+ expect(warnings[0].message).toContain("got number");
82
+ });
83
+
84
+ test("should warn when tags is boolean false", () => {
85
+ const frontmatter = {
86
+ title: "Sample Skill",
87
+ id: "sample-skill",
88
+ tags: false,
89
+ };
90
+
91
+ const warnings = validateTags(frontmatter, testPath);
92
+
93
+ expect(warnings).toHaveLength(1);
94
+ expect(warnings[0].field).toBe("tags");
95
+ // false is defined but wrong type
96
+ expect(warnings[0].message).toContain("'tags' must be an array");
97
+ expect(warnings[0].message).toContain("got boolean");
98
+ });
99
+
100
+ test("should warn when tags is a string", () => {
101
+ const frontmatter = {
102
+ title: "Sample Skill",
103
+ id: "sample-skill",
104
+ tags: "testing, validation",
105
+ };
106
+
107
+ const warnings = validateTags(frontmatter, testPath);
108
+
109
+ expect(warnings).toHaveLength(1);
110
+ expect(warnings[0].field).toBe("tags");
111
+ expect(warnings[0].message).toContain("'tags' must be an array");
112
+ expect(warnings[0].message).toContain("got string");
113
+ });
114
+
115
+ test("should warn when tags is a number", () => {
116
+ const frontmatter = {
117
+ title: "Sample Skill",
118
+ id: "sample-skill",
119
+ tags: 42,
120
+ };
121
+
122
+ const warnings = validateTags(frontmatter, testPath);
123
+
124
+ expect(warnings).toHaveLength(1);
125
+ expect(warnings[0].field).toBe("tags");
126
+ expect(warnings[0].message).toContain("'tags' must be an array");
127
+ expect(warnings[0].message).toContain("got number");
128
+ });
129
+
130
+ test("should warn when tags is an object", () => {
131
+ const frontmatter = {
132
+ title: "Sample Skill",
133
+ id: "sample-skill",
134
+ tags: { tag1: "testing", tag2: "validation" },
135
+ };
136
+
137
+ const warnings = validateTags(frontmatter, testPath);
138
+
139
+ expect(warnings).toHaveLength(1);
140
+ expect(warnings[0].field).toBe("tags");
141
+ expect(warnings[0].message).toContain("'tags' must be an array");
142
+ expect(warnings[0].message).toContain("got object");
143
+ });
144
+
145
+ test("should warn when tags is boolean true", () => {
146
+ const frontmatter = {
147
+ title: "Sample Skill",
148
+ id: "sample-skill",
149
+ tags: true,
150
+ };
151
+
152
+ const warnings = validateTags(frontmatter, testPath);
153
+
154
+ expect(warnings).toHaveLength(1);
155
+ expect(warnings[0].field).toBe("tags");
156
+ expect(warnings[0].message).toContain("'tags' must be an array");
157
+ expect(warnings[0].message).toContain("got boolean");
158
+ });
159
+
160
+ test("should warn when tags is a function", () => {
161
+ const frontmatter = {
162
+ title: "Sample Skill",
163
+ id: "sample-skill",
164
+ tags: () => ["test"],
165
+ };
166
+
167
+ const warnings = validateTags(frontmatter, testPath);
168
+
169
+ expect(warnings).toHaveLength(1);
170
+ expect(warnings[0].field).toBe("tags");
171
+ expect(warnings[0].message).toContain("'tags' must be an array");
172
+ expect(warnings[0].message).toContain("got function");
173
+ });
174
+
175
+ test("should warn when tags is a symbol", () => {
176
+ const frontmatter = {
177
+ title: "Sample Skill",
178
+ id: "sample-skill",
179
+ tags: Symbol("tags"),
180
+ };
181
+
182
+ const warnings = validateTags(frontmatter, testPath);
183
+
184
+ expect(warnings).toHaveLength(1);
185
+ expect(warnings[0].field).toBe("tags");
186
+ expect(warnings[0].message).toContain("'tags' must be an array");
187
+ expect(warnings[0].message).toContain("got symbol");
188
+ });
189
+ });
190
+
191
+ describe("empty tags array", () => {
192
+ test("should warn when tags is an empty array", () => {
193
+ const frontmatter = {
194
+ title: "Sample Skill",
195
+ id: "sample-skill",
196
+ tags: [],
197
+ };
198
+
199
+ const warnings = validateTags(frontmatter, testPath);
200
+
201
+ expect(warnings).toHaveLength(1);
202
+ expect(warnings[0].field).toBe("tags");
203
+ expect(warnings[0].message).toContain("Empty 'tags' array");
204
+ });
205
+ });
206
+
207
+ describe("valid tags array", () => {
208
+ test("should not warn when tags has one element", () => {
209
+ const frontmatter = {
210
+ title: "Sample Skill",
211
+ id: "sample-skill",
212
+ tags: ["testing"],
213
+ };
214
+
215
+ const warnings = validateTags(frontmatter, testPath);
216
+
217
+ expect(warnings).toHaveLength(0);
218
+ });
219
+
220
+ test("should not warn when tags has multiple elements", () => {
221
+ const frontmatter = {
222
+ title: "Sample Skill",
223
+ id: "sample-skill",
224
+ tags: ["testing", "validation", "skills"],
225
+ };
226
+
227
+ const warnings = validateTags(frontmatter, testPath);
228
+
229
+ expect(warnings).toHaveLength(0);
230
+ });
231
+
232
+ test("should not warn when tags contains empty string", () => {
233
+ // Empty string in array is valid from validation perspective
234
+ // (content validation would be separate)
235
+ const frontmatter = {
236
+ title: "Sample Skill",
237
+ id: "sample-skill",
238
+ tags: [""],
239
+ };
240
+
241
+ const warnings = validateTags(frontmatter, testPath);
242
+
243
+ expect(warnings).toHaveLength(0);
244
+ });
245
+
246
+ test("should not warn when tags contains mixed types", () => {
247
+ // Mixed types in array is valid from type validation perspective
248
+ // (content validation would be separate)
249
+ const frontmatter = {
250
+ title: "Sample Skill",
251
+ id: "sample-skill",
252
+ tags: ["testing", 123, null],
253
+ };
254
+
255
+ const warnings = validateTags(frontmatter, testPath);
256
+
257
+ expect(warnings).toHaveLength(0);
258
+ });
259
+ });
260
+
261
+ describe("error message content", () => {
262
+ test("should include skills index reference in missing tags message", () => {
263
+ const frontmatter = { title: "Test" };
264
+
265
+ const warnings = validateTags(frontmatter, testPath);
266
+
267
+ expect(warnings[0].message).toContain("will show empty Tags column in skills index");
268
+ });
269
+
270
+ test("should include skills index reference in wrong type message", () => {
271
+ const frontmatter = { title: "Test", tags: "string" };
272
+
273
+ const warnings = validateTags(frontmatter, testPath);
274
+
275
+ expect(warnings[0].message).toContain("will show empty Tags column in skills index");
276
+ });
277
+
278
+ test("should include skills index reference in empty array message", () => {
279
+ const frontmatter = { title: "Test", tags: [] };
280
+
281
+ const warnings = validateTags(frontmatter, testPath);
282
+
283
+ expect(warnings[0].message).toContain("will show empty Tags column in skills index");
284
+ });
285
+
286
+ test("should include correct file path in warning", () => {
287
+ const customPath = "custom/path/to/skill.md";
288
+ const frontmatter = { title: "Test" };
289
+
290
+ const warnings = validateTags(frontmatter, customPath);
291
+
292
+ expect(warnings[0].file).toBe(customPath);
293
+ });
294
+
295
+ test("should report correct field name", () => {
296
+ const frontmatter = { title: "Test" };
297
+
298
+ const warnings = validateTags(frontmatter, testPath);
299
+
300
+ expect(warnings[0].field).toBe("tags");
301
+ });
302
+ });
303
+
304
+ describe("edge cases", () => {
305
+ test("should handle frontmatter with no properties", () => {
306
+ const frontmatter = {};
307
+
308
+ const warnings = validateTags(frontmatter, testPath);
309
+
310
+ expect(warnings).toHaveLength(1);
311
+ expect(warnings[0].message).toContain("Missing 'tags' array");
312
+ });
313
+
314
+ test("should handle array-like objects", () => {
315
+ // Array-like object (not a real array)
316
+ const frontmatter = {
317
+ title: "Sample Skill",
318
+ id: "sample-skill",
319
+ tags: { 0: "test", 1: "tags", length: 2 },
320
+ };
321
+
322
+ const warnings = validateTags(frontmatter, testPath);
323
+
324
+ expect(warnings).toHaveLength(1);
325
+ expect(warnings[0].message).toContain("'tags' must be an array");
326
+ expect(warnings[0].message).toContain("got object");
327
+ });
328
+
329
+ test("should accept array created with Array constructor", () => {
330
+ const frontmatter = {
331
+ title: "Sample Skill",
332
+ id: "sample-skill",
333
+ tags: new Array("testing", "validation"),
334
+ };
335
+
336
+ const warnings = validateTags(frontmatter, testPath);
337
+
338
+ expect(warnings).toHaveLength(0);
339
+ });
340
+
341
+ test("should handle very long tags array", () => {
342
+ const frontmatter = {
343
+ title: "Sample Skill",
344
+ id: "sample-skill",
345
+ tags: Array.from({ length: 100 }, (_, i) => `tag${i}`),
346
+ };
347
+
348
+ const warnings = validateTags(frontmatter, testPath);
349
+
350
+ expect(warnings).toHaveLength(0);
351
+ });
352
+ });
353
+ });
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: 5e9f3a7b2c4d6e8f0a1b3c5d7e9f1a2b
3
+ DefaultImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant: