coh-content-db 2.0.0-rc.2 → 2.0.0-rc.21

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 (120) hide show
  1. package/.editorconfig +10 -11
  2. package/.github/workflows/build.yml +4 -2
  3. package/.github/workflows/pull-request.yml +1 -1
  4. package/.github/workflows/release.yml +1 -1
  5. package/CHANGELOG.md +47 -0
  6. package/README.md +77 -32
  7. package/dist/coh-content-db.d.ts +755 -290
  8. package/dist/coh-content-db.js +1028 -358
  9. package/dist/coh-content-db.js.map +1 -1
  10. package/dist/coh-content-db.mjs +998 -349
  11. package/dist/coh-content-db.mjs.map +1 -1
  12. package/eslint.config.mjs +1 -0
  13. package/jest.config.mjs +1 -0
  14. package/package.json +14 -14
  15. package/src/main/api/alignment.ts +18 -2
  16. package/src/main/api/badge-data.ts +29 -51
  17. package/src/main/api/badge-requirement-data.ts +64 -0
  18. package/src/main/api/badge-requirement-type.ts +32 -0
  19. package/src/main/api/badge-type.ts +15 -15
  20. package/src/main/api/bundle-data.ts +47 -0
  21. package/src/main/api/bundle-header-data.ts +44 -0
  22. package/src/main/api/contact-data.ts +49 -0
  23. package/src/main/api/enhancement-category.ts +26 -26
  24. package/src/main/api/level-range-data.ts +4 -0
  25. package/src/main/api/location-data.ts +28 -0
  26. package/src/main/api/markdown-string.ts +4 -0
  27. package/src/main/api/mission-data.ts +57 -0
  28. package/src/main/api/mission-flashback-data.ts +31 -0
  29. package/src/main/api/mission-type.ts +2 -0
  30. package/src/main/api/morality.ts +49 -0
  31. package/src/main/api/set-title-data.ts +4 -0
  32. package/src/main/api/sex.ts +8 -1
  33. package/src/main/api/variant-context.ts +11 -0
  34. package/src/main/api/variant-data.ts +22 -0
  35. package/src/main/api/zone-data.ts +44 -0
  36. package/src/main/api/zone-type.ts +59 -0
  37. package/src/main/db/abstract-index.ts +37 -0
  38. package/src/main/db/alignment-list.ts +54 -0
  39. package/src/main/db/badge-index.ts +83 -0
  40. package/src/main/db/badge-requirement.ts +81 -0
  41. package/src/main/db/badge-search-options.ts +52 -0
  42. package/src/main/db/badge.ts +97 -74
  43. package/src/main/db/bundle-header.ts +52 -0
  44. package/src/main/db/coh-content-database.ts +123 -14
  45. package/src/main/db/contact.ts +63 -0
  46. package/src/main/db/level-range.ts +15 -0
  47. package/src/main/db/location.ts +30 -0
  48. package/src/main/db/mission.ts +108 -0
  49. package/src/main/db/morality-list.ts +99 -0
  50. package/src/main/db/paged.ts +11 -0
  51. package/src/main/db/set-title-ids.ts +10 -0
  52. package/src/main/db/variants.ts +84 -0
  53. package/src/main/db/zone.ts +57 -0
  54. package/src/main/index.ts +33 -18
  55. package/src/main/util/coalesce-to-array.ts +13 -0
  56. package/src/main/util/links.ts +104 -0
  57. package/src/main/util/to-date.ts +9 -0
  58. package/src/test/api/alignment.test.ts +38 -4
  59. package/src/test/api/badge-data.fixture.ts +2 -15
  60. package/src/test/api/badge-data.test.ts +5 -4
  61. package/src/test/api/badge-requirement-data.fixture.ts +7 -0
  62. package/src/test/api/badge-requirement-type.test.ts +31 -0
  63. package/src/test/api/badge-type.test.ts +5 -5
  64. package/src/test/api/bundle-data.fixture.ts +7 -0
  65. package/src/test/api/bundle-header-data.fixture.ts +8 -0
  66. package/src/test/api/contact-data.fixture.ts +7 -0
  67. package/src/test/api/enhancement-category.test.ts +5 -5
  68. package/src/test/api/mission-data.fixture.ts +12 -0
  69. package/src/test/api/morality.test.ts +31 -0
  70. package/src/test/api/sex.test.ts +33 -1
  71. package/src/test/api/zone-data.fixture.ts +9 -0
  72. package/src/test/db/abstract-index.test.ts +55 -0
  73. package/src/test/db/alignment-list.test.ts +200 -0
  74. package/src/test/db/badge-index.test.ts +653 -0
  75. package/src/test/db/badge-requirement.test.ts +145 -0
  76. package/src/test/db/badge.test.ts +416 -14
  77. package/src/test/db/bundle-header.test.ts +89 -0
  78. package/src/test/db/coh-content-database.test.ts +265 -24
  79. package/src/test/db/contact.test.ts +98 -0
  80. package/src/test/db/level-range.test.ts +47 -0
  81. package/src/test/db/location.test.ts +51 -0
  82. package/src/test/db/mission.test.ts +173 -0
  83. package/src/test/db/morality-list.test.ts +457 -0
  84. package/src/test/db/set-title-ids.test.ts +19 -0
  85. package/src/test/db/variants.test.ts +188 -0
  86. package/src/test/db/zone.test.ts +81 -0
  87. package/src/test/integration.test.ts +16 -0
  88. package/src/test/util/coalese-to-array.test.ts +17 -0
  89. package/src/test/util/links.test.ts +149 -0
  90. package/src/test/util/to-date.test.ts +15 -0
  91. package/src/main/api/alternate-data.ts +0 -22
  92. package/src/main/api/badge-partial-data.ts +0 -65
  93. package/src/main/api/badge-partial-type.ts +0 -8
  94. package/src/main/api/change.ts +0 -14
  95. package/src/main/api/game-map-data.ts +0 -26
  96. package/src/main/api/plaque-type.ts +0 -6
  97. package/src/main/api/server-group-data.ts +0 -65
  98. package/src/main/api/vidiot-map-data.ts +0 -18
  99. package/src/main/api/vidiot-map-point-of-interest-data.ts +0 -30
  100. package/src/main/changelog.ts +0 -20
  101. package/src/main/db/alternates.ts +0 -81
  102. package/src/main/db/badge-partial.ts +0 -35
  103. package/src/main/db/game-map.ts +0 -33
  104. package/src/main/db/server-group.ts +0 -112
  105. package/src/main/db/vidiot-map-point-of-interest.ts +0 -40
  106. package/src/main/db/vidiot-map.ts +0 -25
  107. package/src/main/util.ts +0 -17
  108. package/src/test/api/badge-partial-data.fixture.ts +0 -17
  109. package/src/test/api/badge-partial-type.test.ts +0 -31
  110. package/src/test/api/game-map-data.fixture.ts +0 -10
  111. package/src/test/api/plaque-type.test.ts +0 -31
  112. package/src/test/api/server-group-data.fixture.ts +0 -23
  113. package/src/test/api/server-group-data.test.ts +0 -15
  114. package/src/test/api/vidiot-map-point-of-interest.fixture.ts +0 -10
  115. package/src/test/api/vidiot-map.fixture.ts +0 -9
  116. package/src/test/changelog.test.ts +0 -36
  117. package/src/test/db/alternates.test.ts +0 -223
  118. package/src/test/db/server-group.test.ts +0 -124
  119. package/src/test/index.test.ts +0 -10
  120. package/src/test/util.test.ts +0 -39
@@ -0,0 +1,145 @@
1
+ import { BadgeRequirement } from '../../main'
2
+ import { badgeRequirementDataFixture } from '../api/badge-requirement-data.fixture'
3
+
4
+ describe(BadgeRequirement.name, () => {
5
+ describe('Constructor', () => {
6
+ test('should accept the test fixture', () => {
7
+ new BadgeRequirement(badgeRequirementDataFixture.create())
8
+ })
9
+ })
10
+
11
+ describe('key', () => {
12
+ test('should be set from the data', () => {
13
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ key: 'foo' }))
14
+ expect(requirement.key).toEqual('foo')
15
+ })
16
+ })
17
+
18
+ describe('type', () => {
19
+ test('should be set from the data', () => {
20
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ type: 'badge' }))
21
+ expect(requirement.type).toEqual('badge')
22
+ })
23
+ })
24
+
25
+ describe('location', () => {
26
+ test('should be set from the data', () => {
27
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ location: { zoneKey: 'foo', coords: [1, 2, 3] } }))
28
+ expect(requirement.location).toStrictEqual([{ zoneKey: 'foo', coords: [1, 2, 3] }])
29
+ })
30
+
31
+ test('should accept an array', () => {
32
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({
33
+ location: [
34
+ { zoneKey: 'foo', coords: [1, 2, 3] },
35
+ { zoneKey: 'bar', coords: [4, 5, 6] },
36
+ ],
37
+ }))
38
+ expect(requirement.location).toStrictEqual([
39
+ { zoneKey: 'foo', coords: [1, 2, 3] },
40
+ { zoneKey: 'bar', coords: [4, 5, 6] },
41
+ ])
42
+ })
43
+
44
+ test('should be optional', () => {
45
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('location').create())
46
+ expect(requirement.location).toBeUndefined()
47
+ })
48
+ })
49
+
50
+ describe('badgeKey', () => {
51
+ test('should be set from the data', () => {
52
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ badgeKey: 'foo' }))
53
+ expect(requirement.badgeKey).toEqual('foo')
54
+ })
55
+
56
+ test('should be optional', () => {
57
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('badgeKey').create())
58
+ expect(requirement.badgeKey).toBeUndefined()
59
+ })
60
+ })
61
+
62
+ describe('missionKey', () => {
63
+ test('should be set from the data', () => {
64
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ missionKey: 'foo' }))
65
+ expect(requirement.missionKey).toEqual('foo')
66
+ })
67
+
68
+ test('should be optional', () => {
69
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('missionKey').create())
70
+ expect(requirement.missionKey).toBeUndefined()
71
+ })
72
+ })
73
+
74
+ describe('monumentText', () => {
75
+ test('should be set from the data', () => {
76
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ monumentText: 'foo' }))
77
+ expect(requirement.monumentText).toEqual('foo')
78
+ })
79
+
80
+ test('should be optional', () => {
81
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('monumentText').create())
82
+ expect(requirement.monumentText).toBeUndefined()
83
+ })
84
+ })
85
+
86
+ describe('inventionLevel', () => {
87
+ test('should be set from the data', () => {
88
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ inventionLevel: 10 }))
89
+ expect(requirement.inventionLevel).toEqual(10)
90
+ })
91
+
92
+ test('should be optional', () => {
93
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('inventionLevel').create())
94
+ expect(requirement.inventionLevel).toBeUndefined()
95
+ })
96
+ })
97
+
98
+ describe('inventionTypes', () => {
99
+ test('should be set from the data', () => {
100
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ inventionTypes: ['accuracy', 'confuse'] }))
101
+ expect(requirement.inventionTypes).toStrictEqual(['accuracy', 'confuse'])
102
+ })
103
+
104
+ test('should be optional', () => {
105
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('inventionTypes').create())
106
+ expect(requirement.inventionTypes).toBeUndefined()
107
+ })
108
+ })
109
+
110
+ describe('count', () => {
111
+ test('should be set from the data', () => {
112
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ count: 5 }))
113
+ expect(requirement.count).toEqual(5)
114
+ })
115
+
116
+ test('should be optional', () => {
117
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('count').create())
118
+ expect(requirement.count).toBeUndefined()
119
+ })
120
+ })
121
+
122
+ describe('notes', () => {
123
+ test('should be set from the data', () => {
124
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ notes: 'some notes' }))
125
+ expect(requirement.notes).toEqual('some notes')
126
+ })
127
+
128
+ test('should be optional', () => {
129
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('notes').create())
130
+ expect(requirement.notes).toBeUndefined()
131
+ })
132
+ })
133
+
134
+ describe('links', () => {
135
+ test('should be set from the data', () => {
136
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.create({ links: [{ title: 'foo', href: 'bar' }] }))
137
+ expect(requirement.links).toStrictEqual([{ title: 'foo', href: 'bar' }])
138
+ })
139
+
140
+ test('should be optional', () => {
141
+ const requirement = new BadgeRequirement(badgeRequirementDataFixture.omit('links').create())
142
+ expect(requirement.links).toHaveLength(0)
143
+ })
144
+ })
145
+ })
@@ -1,6 +1,6 @@
1
- import { Badge } from '../../main'
1
+ import { Badge, compareByName, compareByReleaseDate, compareByZoneKey } from '../../main'
2
2
  import { badgeDataFixture } from '../api/badge-data.fixture'
3
- import { badgePartialDataFixture } from '../api/badge-partial-data.fixture'
3
+ import { badgeRequirementDataFixture } from '../api/badge-requirement-data.fixture'
4
4
 
5
5
  describe(Badge.name, () => {
6
6
  describe('Constructor', () => {
@@ -9,33 +9,435 @@ describe(Badge.name, () => {
9
9
  })
10
10
  })
11
11
 
12
- describe('partials', () => {
12
+ describe('key', () => {
13
+ test('should be set from the data', () => {
14
+ const badge = new Badge(badgeDataFixture.create({ key: 'badge123' }))
15
+ expect(badge.key).toEqual('badge123')
16
+ })
17
+ })
18
+
19
+ describe('type', () => {
20
+ test('should be set from the data', () => {
21
+ const badge = new Badge(badgeDataFixture.create({ type: 'achievement' }))
22
+ expect(badge.type).toEqual('achievement')
23
+ })
24
+ })
25
+
26
+ describe('name', () => {
27
+ test('should be set from the data', () => {
28
+ const badge = new Badge(badgeDataFixture.create({ name: [{ value: 'foo' }] }))
29
+ expect(badge.name.default).toEqual({ value: 'foo' })
30
+ })
31
+
32
+ test('should accept a string', () => {
33
+ const badge = new Badge(badgeDataFixture.create({ name: 'foo' }))
34
+ expect(badge.name.default).toEqual({ value: 'foo' })
35
+ })
36
+ })
37
+
38
+ describe('releaseDate', () => {
39
+ test('should be set from the data', () => {
40
+ const badge = new Badge(badgeDataFixture.create({ releaseDate: '2025-08-08' }))
41
+ expect(badge.releaseDate).toEqual(new Date('2025-08-08'))
42
+ })
43
+
44
+ test('should not accept an invalid date', () => {
45
+ expect(() =>
46
+ new Badge(badgeDataFixture.create({ releaseDate: '2025-??-08' })),
47
+ ).toThrow('Invalid date')
48
+ })
49
+ })
50
+
51
+ describe('morality', () => {
52
+ test('should be set from the data', () => {
53
+ const badge = new Badge(badgeDataFixture.create({ morality: ['hero', 'villain'] }))
54
+ expect(badge.morality.hero).toBeTruthy()
55
+ expect(badge.morality.villain).toBeTruthy()
56
+ })
57
+
58
+ test('should accept a single string', () => {
59
+ const badge = new Badge(badgeDataFixture.create({ morality: 'hero' }))
60
+ expect(badge.morality.hero).toBeTruthy()
61
+ expect(badge.morality.villain).toBeFalsy()
62
+ })
63
+
64
+ test('should be optional, defaulting to all', () => {
65
+ const badge = new Badge(badgeDataFixture.omit('morality').create())
66
+ expect(badge.morality.all).toBeTruthy()
67
+ })
68
+ })
69
+
70
+ describe('badgeText', () => {
71
+ test('should be set from the data', () => {
72
+ const badge = new Badge(badgeDataFixture.create({ badgeText: [{ value: 'foo' }] }))
73
+ expect(badge.badgeText.default).toEqual({ value: 'foo' })
74
+ })
75
+
76
+ test('should accept a string', () => {
77
+ const badge = new Badge(badgeDataFixture.create({ badgeText: 'foo' }))
78
+ expect(badge.badgeText.default).toEqual({ value: 'foo' })
79
+ })
80
+
81
+ test('should be optional', () => {
82
+ const badge = new Badge(badgeDataFixture.omit('badgeText').create())
83
+ expect(badge.badgeText.default).toBeUndefined()
84
+ })
85
+ })
86
+
87
+ describe('acquisition', () => {
88
+ test('should be set from the data', () => {
89
+ const badge = new Badge(badgeDataFixture.create({ acquisition: 'Quest Reward' }))
90
+ expect(badge.acquisition).toEqual('Quest Reward')
91
+ })
92
+
93
+ test('should be optional', () => {
94
+ const badge = new Badge(badgeDataFixture.omit('acquisition').create())
95
+ expect(badge.acquisition).toBeUndefined()
96
+ })
97
+ })
98
+
99
+ describe('icon', () => {
100
+ test('should be set from the data', () => {
101
+ const badge = new Badge(badgeDataFixture.create({ icon: [{ value: 'foo' }] }))
102
+ expect(badge.icon.default).toEqual({ value: 'foo' })
103
+ })
104
+
105
+ test('should accept a string', () => {
106
+ const badge = new Badge(badgeDataFixture.create({ icon: 'foo' }))
107
+ expect(badge.icon.default).toEqual({ value: 'foo' })
108
+ })
109
+
110
+ test('should be optional', () => {
111
+ const badge = new Badge(badgeDataFixture.omit('icon').create())
112
+ expect(badge.icon.default).toBeUndefined()
113
+ })
114
+ })
115
+
116
+ describe('notes', () => {
117
+ test('should be set from the data', () => {
118
+ const badge = new Badge(badgeDataFixture.create({ notes: 'foo' }))
119
+ expect(badge.notes).toEqual('foo')
120
+ })
121
+
122
+ test('should be optional', () => {
123
+ const badge = new Badge(badgeDataFixture.omit('notes').create())
124
+ expect(badge.notes).toBeUndefined()
125
+ })
126
+ })
127
+
128
+ describe('links', () => {
129
+ test('should be set from the data', () => {
130
+ const badge = new Badge(badgeDataFixture.create({ links: [{ title: 'foo', href: 'bar' }] }))
131
+ expect(badge.links).toStrictEqual([{ title: 'foo', href: 'bar' }])
132
+ })
133
+
134
+ test('should be optional', () => {
135
+ const badge = new Badge(badgeDataFixture.omit('links').create())
136
+ expect(badge.links).toHaveLength(0)
137
+ })
138
+ })
139
+
140
+ describe('effect', () => {
141
+ test('should be set from the data', () => {
142
+ const badge = new Badge(badgeDataFixture.create({ effect: 'foo' }))
143
+ expect(badge.effect).toEqual('foo')
144
+ })
145
+
146
+ test('should be optional', () => {
147
+ const badge = new Badge(badgeDataFixture.omit('effect').create())
148
+ expect(badge.effect).toBeUndefined()
149
+ })
150
+ })
151
+
152
+ describe('setTitle', () => {
153
+ test('should be set from the data', () => {
154
+ const badge = new Badge(badgeDataFixture.create({ setTitleId: [123, 456] }))
155
+ expect(badge.setTitleId?.primal).toEqual(123)
156
+ expect(badge.setTitleId?.praetorian).toEqual(456)
157
+ })
158
+
159
+ test('should treat the praetorian id as optional', () => {
160
+ const badge = new Badge(badgeDataFixture.create({ setTitleId: [123] }))
161
+ expect(badge.setTitleId?.primal).toEqual(123)
162
+ expect(badge.setTitleId?.praetorian).toBeUndefined()
163
+ })
164
+
165
+ test('should be optional', () => {
166
+ const badge = new Badge(badgeDataFixture.omit('setTitleId').create())
167
+ expect(badge.setTitleId).toBeUndefined()
168
+ })
169
+ })
170
+
171
+ describe('ignoreInTotals', () => {
172
+ test('should be set from the data', () => {
173
+ const badge = new Badge(badgeDataFixture.create({ ignoreInTotals: true }))
174
+ expect(badge.ignoreInTotals).toEqual(true)
175
+ })
176
+
177
+ test('should default to false', () => {
178
+ const badge = new Badge(badgeDataFixture.omit('ignoreInTotals').create())
179
+ expect(badge.ignoreInTotals).toEqual(false)
180
+ })
181
+ })
182
+
183
+ describe('requirements', () => {
13
184
  test(`should throw an error on duplicate key`, () => {
14
185
  const data = badgeDataFixture.create({
15
- partials: [
16
- badgePartialDataFixture.create({ key: 'foo' }),
17
- badgePartialDataFixture.create({ key: 'foo' }),
186
+ key: 'badge',
187
+ requirements: [
188
+ badgeRequirementDataFixture.create({ key: 'foo' }),
189
+ badgeRequirementDataFixture.create({ key: 'foo' }),
190
+ ],
191
+ })
192
+ expect(() => new Badge(data)).toThrow('Duplicate key [foo]')
193
+ })
194
+
195
+ test(`should return requirement list`, () => {
196
+ const data = badgeDataFixture.create({
197
+ requirements: [
198
+ badgeRequirementDataFixture.create({ key: 'foo' }),
199
+ badgeRequirementDataFixture.create({ key: 'bar' }),
18
200
  ],
19
201
  })
20
- expect(() => new Badge(data)).toThrow('Duplicate badge partial key [foo]')
202
+ const badge = new Badge(data)
203
+
204
+ expect(badge.requirements.map(x => x.key)).toStrictEqual(['foo', 'bar'])
21
205
  })
22
206
  })
23
207
 
24
- describe('getBadgePartial', () => {
25
- test(`should retrieve partial from the index`, () => {
208
+ describe('getRequirement', () => {
209
+ test(`should retrieve requirement from the index`, () => {
26
210
  const data = badgeDataFixture.create({
27
- partials: [badgePartialDataFixture.create({ key: 'foo' })],
211
+ requirements: [badgeRequirementDataFixture.create({ key: 'foo' })],
28
212
  })
29
213
 
30
- expect(new Badge(data).getPartial('foo')).not.toBeNull()
214
+ expect(new Badge(data).getRequirement('foo')).not.toBeUndefined()
31
215
  })
32
216
 
33
- test(`should throw error for unknown partial`, () => {
217
+ test(`should return undefined for unknown requirement`, () => {
34
218
  const data = badgeDataFixture.create({
35
- partials: [],
219
+ requirements: [],
36
220
  })
37
221
 
38
- expect(() => new Badge(data).getPartial('foo')).toThrow('Unknown badge partial key [foo]')
222
+ expect(new Badge(data).getRequirement('foo')).toBeUndefined()
223
+ })
224
+ })
225
+
226
+ describe('zoneKeys', () => {
227
+ test(`should return the list of keys`, () => {
228
+ const badge = new Badge(badgeDataFixture.create({
229
+ requirements: [
230
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
231
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'c' } }),
232
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'b' } }),
233
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'c' } }),
234
+ ],
235
+ }))
236
+ expect(badge.zoneKeys).toStrictEqual(['a', 'c', 'b'])
237
+ })
238
+
239
+ test(`should return undefined if there no zones`, () => {
240
+ const badge = new Badge(badgeDataFixture.create({
241
+ requirements: [
242
+ badgeRequirementDataFixture.omit('location').create(),
243
+ ],
244
+ }))
245
+ expect(badge.zoneKey).toBeUndefined()
246
+ })
247
+
248
+ test(`should ignore requirements with no location`, () => {
249
+ const badge = new Badge(badgeDataFixture.create({
250
+ requirements: [
251
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
252
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'c' } }),
253
+ badgeRequirementDataFixture.omit('location').create(),
254
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'c' } }),
255
+ ],
256
+ }))
257
+ expect(badge.zoneKeys).toStrictEqual(['a', 'c'])
258
+ })
259
+
260
+ test(`should ignore locations with no zone key`, () => {
261
+ const badge = new Badge(badgeDataFixture.create({
262
+ requirements: [
263
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
264
+ badgeRequirementDataFixture.create({ location: { coords: [1, 2, 3] } }),
265
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'c' } }),
266
+ ],
267
+ }))
268
+ expect(badge.zoneKeys).toStrictEqual(['a', 'c'])
269
+ })
270
+ })
271
+
272
+ describe('zoneKey', () => {
273
+ test(`should return the key for a single zone`, () => {
274
+ const badge = new Badge(badgeDataFixture.create({
275
+ requirements: [
276
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
277
+ ],
278
+ }))
279
+ expect(badge.zoneKey).toBe('a')
280
+ })
281
+
282
+ test(`should return undefined if there no zones`, () => {
283
+ const badge = new Badge(badgeDataFixture.create({
284
+ requirements: [
285
+ badgeRequirementDataFixture.omit('location').create(),
286
+ ],
287
+ }))
288
+ expect(badge.zoneKey).toBeUndefined()
289
+ })
290
+
291
+ test(`should return undefined if there are multiple zones`, () => {
292
+ const badge = new Badge(badgeDataFixture.create({
293
+ requirements: [
294
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
295
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'c' } }),
296
+ ],
297
+ }))
298
+ expect(badge.zoneKey).toBeUndefined()
299
+ })
300
+ })
301
+
302
+ describe(compareByName.name, () => {
303
+ test(`should compare two badges by name`, () => {
304
+ const badgeA = new Badge(badgeDataFixture.create({ name: 'A' }))
305
+ const badgeB = new Badge(badgeDataFixture.create({ name: 'B' }))
306
+ expect(compareByName(badgeA, badgeB)).toBeLessThan(0)
307
+ expect([badgeB, badgeA].toSorted(compareByName)).toStrictEqual([badgeA, badgeB])
308
+ })
309
+
310
+ test(`should return 0 for equal names`, () => {
311
+ const badgeA = new Badge(badgeDataFixture.create({ name: 'A' }))
312
+ const badgeB = new Badge(badgeDataFixture.create({ name: 'A' }))
313
+ expect(compareByName(badgeA, badgeB)).toEqual(0)
314
+ })
315
+
316
+ test(`should compare two undefined values`, () => {
317
+ const badgeA = new Badge(badgeDataFixture.create({ name: [] }))
318
+ const badgeB = new Badge(badgeDataFixture.create({ name: [] }))
319
+ expect(compareByName(badgeA, badgeB)).toEqual(0)
320
+ })
321
+
322
+ test(`should sort undefined values last`, () => {
323
+ const badgeA = new Badge(badgeDataFixture.create({ name: 'A' }))
324
+ const badgeB = new Badge(badgeDataFixture.create({ name: [] }))
325
+ expect([badgeA, badgeB].toSorted(compareByName)).toStrictEqual([badgeA, badgeB])
326
+ expect([badgeB, badgeA].toSorted(compareByName)).toStrictEqual([badgeA, badgeB])
327
+ })
328
+
329
+ test(`should take morality context into account`, () => {
330
+ const badgeA = new Badge(badgeDataFixture.create({ name: [{ value: 'ZZZ' }, { alignment: 'villain', value: 'AAA' }] }))
331
+ const badgeB = new Badge(badgeDataFixture.create({ name: 'B' }))
332
+ expect([badgeA, badgeB].toSorted((a, b) => compareByName(a, b, { morality: 'villain' }))).toStrictEqual([badgeA, badgeB])
333
+ expect([badgeA, badgeB].toSorted((a, b) => compareByName(a, b, {}))).toStrictEqual([badgeB, badgeA])
334
+ })
335
+
336
+ test(`should take sex context into account`, () => {
337
+ const badgeA = new Badge(badgeDataFixture.create({ name: [{ value: 'ZZZ' }, { sex: 'F', value: 'AAA' }] }))
338
+ const badgeB = new Badge(badgeDataFixture.create({ name: 'B' }))
339
+ expect([badgeA, badgeB].toSorted((a, b) => compareByName(a, b, { sex: 'F' }))).toStrictEqual([badgeA, badgeB])
340
+ expect([badgeA, badgeB].toSorted((a, b) => compareByName(a, b, {}))).toStrictEqual([badgeB, badgeA])
341
+ })
342
+
343
+ test(`should take full variant context into account`, () => {
344
+ const badgeA = new Badge(badgeDataFixture.create({ name: [{ value: 'A' }, { sex: 'F', value: 'B' }] }))
345
+ const badgeB = new Badge(badgeDataFixture.create({ name: [{ value: 'B' }, { alignment: 'praetorian', sex: 'F', value: 'A' }] }))
346
+ const badgeC = new Badge(badgeDataFixture.create({ name: 'C' }))
347
+ expect([badgeA, badgeB, badgeC].toSorted((a, b) => compareByName(a, b, { morality: 'resistance', sex: 'F' }))).toStrictEqual([badgeB, badgeA, badgeC])
348
+ expect([badgeA, badgeB, badgeC].toSorted((a, b) => compareByName(a, b, {}))).toStrictEqual([badgeA, badgeB, badgeC])
349
+ })
350
+ })
351
+
352
+ describe(compareByZoneKey.name, () => {
353
+ test(`should compare two badges by zoneKey`, () => {
354
+ const badgeA = new Badge(badgeDataFixture.create({
355
+ requirements: [
356
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
357
+ ],
358
+ }))
359
+ const badgeB = new Badge(badgeDataFixture.create({
360
+ requirements: [
361
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'b' } }),
362
+ ],
363
+ }))
364
+ expect(compareByZoneKey(badgeA, badgeB)).toBeLessThan(0)
365
+ expect([badgeB, badgeA].toSorted(compareByZoneKey)).toStrictEqual([badgeA, badgeB])
366
+ })
367
+
368
+ test(`should return 0 for equal zoneKeys`, () => {
369
+ const badgeA = new Badge(badgeDataFixture.create({
370
+ requirements: [
371
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
372
+ ],
373
+ }))
374
+ const badgeB = new Badge(badgeDataFixture.create({
375
+ requirements: [
376
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
377
+ ],
378
+ }))
379
+ expect(compareByZoneKey(badgeA, badgeB)).toEqual(0)
380
+ })
381
+
382
+ test(`should equate two undefined values`, () => {
383
+ const badgeA = new Badge(badgeDataFixture.create({
384
+ requirements: [
385
+ badgeRequirementDataFixture.omit('location').create(),
386
+ ],
387
+ }))
388
+ const badgeB = new Badge(badgeDataFixture.create({
389
+ requirements: [
390
+ badgeRequirementDataFixture.omit('location').create(),
391
+ ],
392
+ }))
393
+ expect(compareByZoneKey(badgeA, badgeB)).toEqual(0)
394
+ })
395
+
396
+ test(`should sort badges with multiple values last`, () => {
397
+ const badgeA = new Badge(badgeDataFixture.create({
398
+ requirements: [
399
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'a' } }),
400
+ ],
401
+ }))
402
+ const badgeB = new Badge(badgeDataFixture.create({
403
+ requirements: [
404
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'b' } }),
405
+ badgeRequirementDataFixture.create({ location: { zoneKey: 'c' } }),
406
+ ],
407
+ }))
408
+ expect([badgeA, badgeB].toSorted(compareByZoneKey)).toStrictEqual([badgeA, badgeB])
409
+ expect([badgeB, badgeA].toSorted(compareByZoneKey)).toStrictEqual([badgeA, badgeB])
410
+ })
411
+ })
412
+
413
+ describe(compareByReleaseDate.name, () => {
414
+ test(`should compare two badges by releaseDate`, () => {
415
+ const badgeA = new Badge(badgeDataFixture.create({ releaseDate: '2024-01-01' }))
416
+ const badgeB = new Badge(badgeDataFixture.create({ releaseDate: '2025-01-01' }))
417
+ expect(compareByReleaseDate(badgeA, badgeB)).toBeLessThan(0)
418
+ expect([badgeB, badgeA].toSorted(compareByReleaseDate)).toStrictEqual([badgeA, badgeB])
419
+ })
420
+
421
+ test(`should return 0 for equal releaseDates`, () => {
422
+ const badgeA = new Badge(badgeDataFixture.create({ releaseDate: '2025-01-01' }))
423
+ const badgeB = new Badge(badgeDataFixture.create({ releaseDate: '2025-01-01' }))
424
+ expect(compareByReleaseDate(badgeA, badgeB)).toEqual(0)
425
+ })
426
+
427
+ test(`should equate two undefined values`, () => {
428
+ const badgeA = undefined
429
+ const badgeB = undefined
430
+ expect(compareByReleaseDate(badgeA, badgeB)).toEqual(0)
431
+ })
432
+
433
+ test(`should compare undefined value as higher`, () => {
434
+ const badgeA = undefined
435
+ const badgeB = new Badge(badgeDataFixture.create({ releaseDate: '2025-01-01' }))
436
+ expect(compareByReleaseDate(badgeA, badgeB)).toBeGreaterThan(0)
437
+ expect([badgeA, badgeB].toSorted(compareByReleaseDate)).toStrictEqual([badgeB, badgeA])
438
+
439
+ expect(compareByReleaseDate(badgeB, badgeA)).toBeLessThan(0)
440
+ expect([badgeB, badgeA].toSorted(compareByReleaseDate)).toStrictEqual([badgeB, badgeA])
39
441
  })
40
442
  })
41
443
  })
@@ -0,0 +1,89 @@
1
+ import { BundleHeader } from '../../main'
2
+ import { bundleHeaderDataFixture } from '../api/bundle-header-data.fixture'
3
+
4
+ describe(BundleHeader.name, () => {
5
+ describe('name', () => {
6
+ test(`should be set from the data`, () => {
7
+ const header = new BundleHeader(bundleHeaderDataFixture.create({ name: 'foo' }))
8
+ expect(header.name).toEqual('foo')
9
+ })
10
+
11
+ test(`should be optional`, () => {
12
+ const header = new BundleHeader(bundleHeaderDataFixture.omit('name').create())
13
+ expect(header.name).toBeUndefined()
14
+ })
15
+ })
16
+
17
+ describe('version', () => {
18
+ test(`should be set from the data`, () => {
19
+ const header = new BundleHeader(bundleHeaderDataFixture.create({ version: 'foo' }))
20
+ expect(header.version).toEqual('foo')
21
+ })
22
+
23
+ test(`should be optional`, () => {
24
+ const header = new BundleHeader(bundleHeaderDataFixture.omit('version').create())
25
+ expect(header.version).toBeUndefined()
26
+ })
27
+ })
28
+
29
+ describe('releaseDate', () => {
30
+ test('should be set from the data', () => {
31
+ const header = new BundleHeader(bundleHeaderDataFixture.create({ lastUpdateTime: '2025-04-21T01:02:03Z' }))
32
+ expect(header.lastUpdateTime).toStrictEqual(new Date('2025-04-21T01:02:03Z'))
33
+ })
34
+
35
+ test('should not accept an invalid date', () => {
36
+ expect(() =>
37
+ new BundleHeader(bundleHeaderDataFixture.create({ lastUpdateTime: 'blah' })),
38
+ ).toThrow('Invalid date')
39
+ })
40
+ })
41
+
42
+ describe('description', () => {
43
+ test(`should be set from the data`, () => {
44
+ const header = new BundleHeader(bundleHeaderDataFixture.create({ description: 'foo' }))
45
+ expect(header.description).toEqual('foo')
46
+ })
47
+
48
+ test(`should be optional`, () => {
49
+ const header = new BundleHeader(bundleHeaderDataFixture.omit('description').create())
50
+ expect(header.description).toBeUndefined()
51
+ })
52
+ })
53
+
54
+ describe('repositoryUrl', () => {
55
+ test(`should be set from the data`, () => {
56
+ const header = new BundleHeader(bundleHeaderDataFixture.create({ repositoryUrl: 'foo' }))
57
+ expect(header.repositoryUrl).toEqual('foo')
58
+ })
59
+
60
+ test(`should be optional`, () => {
61
+ const header = new BundleHeader(bundleHeaderDataFixture.omit('repositoryUrl').create())
62
+ expect(header.repositoryUrl).toBeUndefined()
63
+ })
64
+ })
65
+
66
+ describe('changelogUrl', () => {
67
+ test(`should be set from the data`, () => {
68
+ const header = new BundleHeader(bundleHeaderDataFixture.create({ changelogUrl: 'foo' }))
69
+ expect(header.changelogUrl).toEqual('foo')
70
+ })
71
+
72
+ test(`should be optional`, () => {
73
+ const header = new BundleHeader(bundleHeaderDataFixture.omit('changelogUrl').create())
74
+ expect(header.changelogUrl).toBeUndefined()
75
+ })
76
+ })
77
+
78
+ describe('links', () => {
79
+ test(`should be set from the data`, () => {
80
+ const header = new BundleHeader(bundleHeaderDataFixture.create({ links: [{ title: 'foo', href: 'bar' }] }))
81
+ expect(header.links).toStrictEqual([{ title: 'foo', href: 'bar' }])
82
+ })
83
+
84
+ test(`should be optional`, () => {
85
+ const header = new BundleHeader(bundleHeaderDataFixture.omit('links').create())
86
+ expect(header.links).toHaveLength(0)
87
+ })
88
+ })
89
+ })