@axiapps/gw2-data 0.1.1 → 0.1.3

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 (43) hide show
  1. package/data/overrides.json +24 -0
  2. package/package.json +4 -1
  3. package/src/engine/attributes.js +102 -23
  4. package/src/engine/boons.js +19 -1
  5. package/src/engine/constants.js +27 -2
  6. package/src/engine/index.js +4 -4
  7. package/src/engine/modifiers.js +57 -21
  8. package/src/index.js +5 -0
  9. package/src/wiki/parser.js +17 -9
  10. package/scripts/generate-fixtures.js +0 -242
  11. package/tests/api-client.test.js +0 -138
  12. package/tests/cache.test.js +0 -108
  13. package/tests/engine/attributes.test.js +0 -252
  14. package/tests/engine/boons.test.js +0 -129
  15. package/tests/engine/combos.test.js +0 -76
  16. package/tests/engine/constants.test.js +0 -576
  17. package/tests/engine/fixtures/berserker-thief.json +0 -61
  18. package/tests/engine/fixtures/berserker-warrior.json +0 -113
  19. package/tests/engine/fixtures/celestial-firebrand-wvw.json +0 -94
  20. package/tests/engine/fixtures/harrier-druid.json +0 -119
  21. package/tests/engine/fixtures/viper-mirage.json +0 -104
  22. package/tests/engine/graph.test.js +0 -30
  23. package/tests/engine/integration.test.js +0 -111
  24. package/tests/engine/modifiers.test.js +0 -473
  25. package/tests/engine/overrides.test.js +0 -70
  26. package/tests/engine/snapshot.test.js +0 -53
  27. package/tests/engine/test-utils.js +0 -20
  28. package/tests/engine/tooltips.test.js +0 -62
  29. package/tests/fixtures/capture.js +0 -160
  30. package/tests/fixtures/fixtures.json +0 -839
  31. package/tests/integration.test.js +0 -100
  32. package/tests/match.test.js +0 -176
  33. package/tests/merge.test.js +0 -128
  34. package/tests/normalize.test.js +0 -78
  35. package/tests/parser.test.js +0 -506
  36. package/tests/real-data.test.js +0 -296
  37. package/tests/relations.test.js +0 -80
  38. package/tests/resolver.test.js +0 -721
  39. package/tests/validate-live.js +0 -191
  40. package/tests/wiki-client.test.js +0 -468
  41. package/tests/wiki-integration.test.js +0 -177
  42. package/tests/wiki-live-validation.test.js +0 -61
  43. package/tests/wiki-snapshots.test.js +0 -166
@@ -1,506 +0,0 @@
1
- "use strict";
2
-
3
- const {
4
- parseSplitGrouping,
5
- parseWikitextFacts,
6
- mapWikiFactToApiFact,
7
- parseInfoboxParams,
8
- splitRespectingTemplates,
9
- } = require("../src/wiki/parser");
10
-
11
- describe("splitRespectingTemplates", () => {
12
- test("splits simple pipe-delimited string", () => {
13
- expect(splitRespectingTemplates("damage|0.8")).toEqual(["damage", "0.8"]);
14
- });
15
-
16
- test("preserves nested templates", () => {
17
- expect(splitRespectingTemplates("healing|{{fraction|7.5}}")).toEqual([
18
- "healing",
19
- "{{fraction|7.5}}",
20
- ]);
21
- });
22
-
23
- test("handles multiple nested templates", () => {
24
- const result = splitRespectingTemplates("damage|{{fraction|0.5}}|hits={{fraction|3}}");
25
- expect(result).toEqual(["damage", "{{fraction|0.5}}", "hits={{fraction|3}}"]);
26
- });
27
- });
28
-
29
- describe("parseSplitGrouping", () => {
30
- test("pve, wvw, pvp → WvW has its own split", () => {
31
- const result = parseSplitGrouping("pve, wvw, pvp");
32
- expect(result).toEqual({ wvwHasSplit: true, wvwGroupedWithPvp: false });
33
- });
34
-
35
- test("pve, wvw pvp → WvW grouped with PvP", () => {
36
- const result = parseSplitGrouping("pve, wvw pvp");
37
- expect(result).toEqual({ wvwHasSplit: true, wvwGroupedWithPvp: true });
38
- });
39
-
40
- test("pve wvw, pvp → WvW grouped with PvE (no actual WvW split)", () => {
41
- const result = parseSplitGrouping("pve wvw, pvp");
42
- expect(result).toEqual({ wvwHasSplit: false, wvwGroupedWithPvp: false });
43
- });
44
-
45
- test("returns no split for null/empty input", () => {
46
- expect(parseSplitGrouping("")).toEqual({ wvwHasSplit: false, wvwGroupedWithPvp: false });
47
- expect(parseSplitGrouping(null)).toEqual({ wvwHasSplit: false, wvwGroupedWithPvp: false });
48
- });
49
-
50
- test("pve, pvp → no WvW at all means no split", () => {
51
- const result = parseSplitGrouping("pve, pvp");
52
- expect(result).toEqual({ wvwHasSplit: false, wvwGroupedWithPvp: false });
53
- });
54
- });
55
-
56
- describe("mapWikiFactToApiFact", () => {
57
- test("damage with coefficient", () => {
58
- const fact = mapWikiFactToApiFact("damage", [], { coefficient: "0.8" }, true, false);
59
- expect(fact).toEqual({
60
- type: "Damage",
61
- text: "Damage",
62
- dmg_multiplier: 0.8,
63
- hit_count: 1,
64
- });
65
- });
66
-
67
- test("damage with coefficient and hits", () => {
68
- const fact = mapWikiFactToApiFact(
69
- "damage",
70
- [],
71
- { coefficient: "0.5", hits: "3" },
72
- true,
73
- false
74
- );
75
- expect(fact).toEqual({
76
- type: "Damage",
77
- text: "Damage",
78
- dmg_multiplier: 0.5,
79
- hit_count: 3,
80
- });
81
- });
82
-
83
- test("recharge with positional value", () => {
84
- const fact = mapWikiFactToApiFact("recharge", ["25"], {}, true, false);
85
- expect(fact).toEqual({ type: "Recharge", text: "Recharge", value: 25 });
86
- });
87
-
88
- test("buff with status, duration, and stacks", () => {
89
- const fact = mapWikiFactToApiFact(
90
- "might",
91
- ["5"],
92
- { stacks: "3" },
93
- true,
94
- false
95
- );
96
- expect(fact).toEqual({
97
- type: "Buff",
98
- text: "Might",
99
- status: "Might",
100
- duration: 5,
101
- apply_count: 3,
102
- });
103
- });
104
-
105
- test("buff defaults to 1 stack", () => {
106
- const fact = mapWikiFactToApiFact("fury", ["8"], {}, true, false);
107
- expect(fact).toEqual({
108
- type: "Buff",
109
- text: "Fury",
110
- status: "Fury",
111
- duration: 8,
112
- apply_count: 1,
113
- });
114
- });
115
-
116
- test("range with valid value", () => {
117
- const fact = mapWikiFactToApiFact("range", ["900"], {}, true, false);
118
- expect(fact).toEqual({ type: "Range", text: "Range", value: 900 });
119
- });
120
-
121
- test("range rejects value <= 1 (boolean flag artifact)", () => {
122
- const fact = mapWikiFactToApiFact("range", ["1"], {}, true, false);
123
- expect(fact).toBeNull();
124
- });
125
-
126
- test("targets with value", () => {
127
- const fact = mapWikiFactToApiFact("targets", ["5"], {}, true, false);
128
- expect(fact).toEqual({ type: "Number", text: "Number of Targets", value: 5 });
129
- });
130
-
131
- test("radius with distance value", () => {
132
- const fact = mapWikiFactToApiFact("radius", ["240"], {}, true, false);
133
- expect(fact).toEqual({ type: "Radius", text: "Radius", distance: 240 });
134
- });
135
-
136
- test("duration with seconds value", () => {
137
- const fact = mapWikiFactToApiFact("duration", ["5"], {}, true, false);
138
- expect(fact).toEqual({ type: "Time", text: "Duration", duration: 5 });
139
- });
140
-
141
- test("healing with base and coefficient", () => {
142
- const fact = mapWikiFactToApiFact(
143
- "healing",
144
- [],
145
- { base: "352", coefficient: "0.5" },
146
- true,
147
- false
148
- );
149
- expect(fact).toMatchObject({
150
- type: "AttributeAdjust",
151
- target: "Healing",
152
- value: 352,
153
- coefficient: 0.5,
154
- });
155
- });
156
-
157
- test("conditions removed", () => {
158
- const fact = mapWikiFactToApiFact("conditions removed", ["2"], {}, true, false);
159
- expect(fact).toEqual({
160
- type: "Number",
161
- text: "Conditions Removed",
162
- value: 2,
163
- });
164
- });
165
-
166
- test("combo finisher", () => {
167
- const fact = mapWikiFactToApiFact("combo", ["blast"], {}, true, false);
168
- expect(fact).toEqual({
169
- type: "ComboFinisher",
170
- text: "Combo Finisher",
171
- finisher_type: "Blast",
172
- });
173
- });
174
-
175
- test("stun break", () => {
176
- const fact = mapWikiFactToApiFact("stun break", [], {}, true, false);
177
- expect(fact).toEqual({ type: "StunBreak", text: "Stun Break", value: true });
178
- });
179
-
180
- test("percent with value", () => {
181
- const fact = mapWikiFactToApiFact("percent", ["20"], {}, true, false);
182
- expect(fact).toEqual({ type: "Percent", text: "Percent", percent: 20 });
183
- });
184
-
185
- test("recharge reduced preserves text", () => {
186
- const fact = mapWikiFactToApiFact("recharge reduced", ["15"], {}, true, false);
187
- expect(fact).toEqual({ type: "Percent", text: "Recharge Reduced", percent: 15 });
188
- });
189
-
190
- test("attribute gain/conversion", () => {
191
- const fact = mapWikiFactToApiFact(
192
- "gain",
193
- [],
194
- { source: "Vitality", target: "Power", percent: "13" },
195
- true,
196
- false
197
- );
198
- expect(fact).toMatchObject({
199
- type: "BuffConversion",
200
- source: "Vitality",
201
- target: "Power",
202
- percent: 13,
203
- });
204
- });
205
-
206
- test("attribute gain/conversion with positional params", () => {
207
- // {{skill fact|Gain|Ferocity|Precision|12}} → positional[0]=Ferocity (target),
208
- // positional[1]=Precision (source), positional[2]=12 (percent)
209
- const fact = mapWikiFactToApiFact(
210
- "gain",
211
- ["Ferocity", "Precision", "12"],
212
- {},
213
- false,
214
- false
215
- );
216
- expect(fact).toMatchObject({
217
- type: "BuffConversion",
218
- source: "Precision",
219
- target: "Ferocity",
220
- percent: 12,
221
- });
222
- });
223
-
224
- test("attribute gain/conversion normalizes wiki attribute names", () => {
225
- // {{skill fact|Gain|Condition Damage|Power|15}} — "Condition Damage" → "ConditionDamage"
226
- const fact = mapWikiFactToApiFact(
227
- "gain",
228
- ["Condition Damage", "Power", "15"],
229
- {},
230
- false,
231
- false
232
- );
233
- expect(fact).toMatchObject({
234
- type: "BuffConversion",
235
- source: "Power",
236
- target: "ConditionDamage",
237
- percent: 15,
238
- });
239
- });
240
-
241
- test("flat attribute bonus produces AttributeAdjust", () => {
242
- const fact = mapWikiFactToApiFact(
243
- "attribute",
244
- ["Concentration", "120"],
245
- {},
246
- true,
247
- false
248
- );
249
- expect(fact).toEqual({
250
- type: "AttributeAdjust",
251
- text: "Concentration",
252
- target: "Concentration",
253
- value: 120,
254
- });
255
- });
256
-
257
- test("flat attribute bonus normalizes multi-word attribute names", () => {
258
- // {{skill fact|attribute|Condition Damage|300}} — "Condition Damage" → "ConditionDamage"
259
- const fact = mapWikiFactToApiFact(
260
- "attribute",
261
- ["Condition Damage", "300"],
262
- {},
263
- true,
264
- false
265
- );
266
- expect(fact).toMatchObject({
267
- type: "AttributeAdjust",
268
- target: "ConditionDamage",
269
- value: 300,
270
- });
271
- });
272
-
273
- test("effect produces Buff with status from positional[0] and duration from positional[1]", () => {
274
- const fact = mapWikiFactToApiFact("effect", ["Superspeed", "5"], { stacks: "2" }, true, false);
275
- expect(fact).toEqual({
276
- type: "Buff",
277
- text: "Superspeed",
278
- status: "Superspeed",
279
- duration: 5,
280
- apply_count: 2,
281
- });
282
- });
283
-
284
- test("effect with no positionals returns empty status and zero duration", () => {
285
- const fact = mapWikiFactToApiFact("effect", [], {}, false, true);
286
- expect(fact).toEqual({
287
- type: "Buff",
288
- text: "",
289
- status: "",
290
- duration: 0,
291
- apply_count: 1,
292
- });
293
- });
294
-
295
- test("effect with desc captures description (signet passive)", () => {
296
- const fact = mapWikiFactToApiFact("effect", ["Signet of Fury (effect)"], { desc: "180 Precision" }, false, true);
297
- expect(fact).toEqual({
298
- type: "Buff",
299
- text: "Signet of Fury (effect)",
300
- status: "Signet of Fury (effect)",
301
- duration: 0,
302
- apply_count: 1,
303
- description: "180 Precision",
304
- });
305
- });
306
-
307
- test("effect with alt and desc (signet active)", () => {
308
- const fact = mapWikiFactToApiFact("effect", ["Signet of Fury (effect)", "4"], { alt: "Active Bonus", desc: "360 Precision, 360 Ferocity" }, false, true);
309
- expect(fact).toEqual({
310
- type: "Buff",
311
- text: "Active Bonus",
312
- status: "Signet of Fury (effect)",
313
- duration: 4,
314
- apply_count: 1,
315
- description: "360 Precision, 360 Ferocity",
316
- });
317
- });
318
-
319
- test("barrier with base and coefficient", () => {
320
- const fact = mapWikiFactToApiFact("barrier", [], { base: "200", coefficient: "0.3" }, true, false);
321
- expect(fact).toMatchObject({
322
- type: "AttributeAdjust",
323
- text: "Barrier",
324
- target: "Barrier",
325
- value: 200,
326
- coefficient: 0.3,
327
- });
328
- });
329
-
330
- test("defiance break with value", () => {
331
- const fact = mapWikiFactToApiFact("defiance break", ["300"], {}, true, false);
332
- expect(fact).toEqual({ type: "Number", text: "Defiance Break", value: 300 });
333
- });
334
-
335
- test("defiance bar variant", () => {
336
- const fact = mapWikiFactToApiFact("defiance bar", ["150"], {}, true, false);
337
- expect(fact).toEqual({ type: "Number", text: "Defiance Break", value: 150 });
338
- });
339
-
340
- test("combo field", () => {
341
- const fact = mapWikiFactToApiFact("combo", ["fire"], {}, true, false);
342
- expect(fact).toEqual({ type: "ComboField", text: "Combo Field", field_type: "Fire" });
343
- });
344
-
345
- test("unblockable", () => {
346
- const fact = mapWikiFactToApiFact("unblockable", [], {}, true, false);
347
- expect(fact).toEqual({ type: "Unblockable", text: "Unblockable", value: true });
348
- });
349
-
350
- test("condition (burning) with stacks", () => {
351
- const fact = mapWikiFactToApiFact("burning", ["3"], { stacks: "2" }, true, false);
352
- expect(fact).toEqual({
353
- type: "Buff",
354
- text: "Burning",
355
- status: "Burning",
356
- duration: 3,
357
- apply_count: 2,
358
- });
359
- });
360
-
361
- test("unknown fact type with numeric value produces generic Number fact", () => {
362
- const fact = mapWikiFactToApiFact("some unknown type", ["15"], {}, true, false);
363
- expect(fact).toEqual({ type: "Number", text: "Some Unknown Type", value: 15 });
364
- });
365
-
366
- test("attack speed increase produces Percent fact", () => {
367
- const fact = mapWikiFactToApiFact("attack speed increase", ["15"], {}, true, false);
368
- expect(fact).toEqual({ type: "Percent", text: "Attack Speed Increase", percent: 15 });
369
- });
370
-
371
- test("unknown fact type with no numeric value returns null", () => {
372
- const fact = mapWikiFactToApiFact("nonexistent_type_xyz", [], {}, true, false);
373
- expect(fact).toBeNull();
374
- });
375
-
376
- test("returns null for known skip types", () => {
377
- expect(mapWikiFactToApiFact("text", [], {}, true, false)).toBeNull();
378
- expect(mapWikiFactToApiFact("combat", [], {}, true, false)).toBeNull();
379
- expect(mapWikiFactToApiFact("misc", [], {}, true, false)).toBeNull();
380
- expect(mapWikiFactToApiFact("pierces", [], {}, true, false)).toBeNull();
381
- });
382
- });
383
-
384
- describe("parseInfoboxParams", () => {
385
- test("extracts recharge wvw param", () => {
386
- const wikitext = "| recharge wvw = 25\n| recharge = 20";
387
- const result = parseInfoboxParams(wikitext, false);
388
- expect(result).toEqual([{ type: "Recharge", text: "Recharge", value: 25 }]);
389
- });
390
-
391
- test("extracts pvp params when WvW grouped with PvP", () => {
392
- const wikitext = "| recharge pvp = 30\n| recharge = 20";
393
- const result = parseInfoboxParams(wikitext, true);
394
- expect(result).toEqual([{ type: "Recharge", text: "Recharge", value: 30 }]);
395
- });
396
-
397
- test("returns empty array when no WvW params", () => {
398
- const wikitext = "| recharge = 20\n| activation = 0.5";
399
- const result = parseInfoboxParams(wikitext, false);
400
- expect(result).toEqual([]);
401
- });
402
- });
403
-
404
- describe("parseWikitextFacts", () => {
405
- test("extracts WvW-specific skill facts", () => {
406
- const wikitext = [
407
- "{{skill fact|damage|0.8|game mode=wvw}}",
408
- "{{skill fact|damage|1.2|game mode=pve}}",
409
- ].join("\n");
410
- const result = parseWikitextFacts(wikitext, false);
411
- expect(result.facts).toHaveLength(1);
412
- expect(result.facts[0].dmg_multiplier).toBe(0.8);
413
- });
414
-
415
- test("extracts universal facts (no game mode)", () => {
416
- const wikitext = "{{skill fact|recharge|25}}";
417
- const result = parseWikitextFacts(wikitext, false);
418
- expect(result.facts).toHaveLength(1);
419
- expect(result.facts[0].value).toBe(25);
420
- });
421
-
422
- test("extracts PvP facts when WvW grouped with PvP", () => {
423
- const wikitext = "{{skill fact|damage|0.6|game mode=pvp}}";
424
- const result = parseWikitextFacts(wikitext, true);
425
- expect(result.facts).toHaveLength(1);
426
- expect(result.facts[0].dmg_multiplier).toBe(0.6);
427
- });
428
-
429
- test("detects PvE-only facts", () => {
430
- const wikitext = [
431
- "{{skill fact|damage|1.5|game mode=pve}}",
432
- "{{skill fact|recharge|20}}",
433
- ].join("\n");
434
- const result = parseWikitextFacts(wikitext, false);
435
- expect(result.hasPveOnly).toBe(true);
436
- });
437
-
438
- test("handles trait fact templates", () => {
439
- const wikitext = "{{trait fact|damage|0.5|game mode=wvw}}";
440
- const result = parseWikitextFacts(wikitext, false);
441
- expect(result.facts).toHaveLength(1);
442
- expect(result.facts[0].dmg_multiplier).toBe(0.5);
443
- });
444
- });
445
-
446
- describe("parseAllTaggedFacts", () => {
447
- const { parseAllTaggedFacts } = require("../src/wiki/parser");
448
-
449
- test("universal facts have all three modes", () => {
450
- const wikitext = "{{skill fact|damage|0.8}}";
451
- const { facts } = parseAllTaggedFacts(wikitext);
452
- expect(facts).toHaveLength(1);
453
- expect(facts[0]._modes).toEqual(["pve", "wvw", "pvp"]);
454
- expect(facts[0].type).toBe("Damage");
455
- });
456
-
457
- test("pve-only fact tagged with ['pve']", () => {
458
- const wikitext = "{{skill fact|damage|0.8|game mode=pve}}";
459
- const { facts, hasPveOnly } = parseAllTaggedFacts(wikitext);
460
- expect(facts).toHaveLength(1);
461
- expect(facts[0]._modes).toEqual(["pve"]);
462
- expect(hasPveOnly).toBe(true);
463
- });
464
-
465
- test("wvw-only fact tagged with ['wvw']", () => {
466
- const wikitext = "{{skill fact|damage|0.5|game mode=wvw}}";
467
- const { facts } = parseAllTaggedFacts(wikitext);
468
- expect(facts).toHaveLength(1);
469
- expect(facts[0]._modes).toEqual(["wvw"]);
470
- });
471
-
472
- test("pvp-only fact tagged with ['pvp']", () => {
473
- const wikitext = "{{skill fact|damage|0.5|game mode=pvp}}";
474
- const { facts } = parseAllTaggedFacts(wikitext);
475
- expect(facts).toHaveLength(1);
476
- expect(facts[0]._modes).toEqual(["pvp"]);
477
- });
478
-
479
- test("compound mode 'pvp wvw' tagged with both", () => {
480
- const wikitext = "{{skill fact|damage|0.5|game mode=pvp wvw}}";
481
- const { facts } = parseAllTaggedFacts(wikitext);
482
- expect(facts).toHaveLength(1);
483
- expect(facts[0]._modes).toEqual(["wvw", "pvp"]);
484
- });
485
-
486
- test("mixed universal and mode-specific facts", () => {
487
- const wikitext = [
488
- "{{skill fact|damage|1.0}}",
489
- "{{skill fact|burning|3|game mode=pve}}",
490
- "{{skill fact|burning|2|game mode=wvw pvp}}",
491
- ].join("\n");
492
- const { facts, hasPveOnly } = parseAllTaggedFacts(wikitext);
493
- expect(facts).toHaveLength(3);
494
- expect(facts[0]._modes).toEqual(["pve", "wvw", "pvp"]); // universal damage
495
- expect(facts[1]._modes).toEqual(["pve"]); // pve burning
496
- expect(facts[2]._modes).toEqual(["wvw", "pvp"]); // wvw+pvp burning
497
- expect(hasPveOnly).toBe(true);
498
- });
499
-
500
- test("all three modes specified individually", () => {
501
- const wikitext = "{{skill fact|recharge|15|game mode=pve wvw pvp}}";
502
- const { facts } = parseAllTaggedFacts(wikitext);
503
- expect(facts).toHaveLength(1);
504
- expect(facts[0]._modes).toEqual(["pve", "wvw", "pvp"]);
505
- });
506
- });