@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,177 +0,0 @@
1
- "use strict";
2
-
3
- const { resolveEntityFacts, parseFactsByMode } = require("../src/wiki/resolver");
4
- const { WikiClient } = require("../src/wiki/client");
5
- const { MemoryCache } = require("../src/wiki/cache");
6
-
7
- describe("Wiki fact resolution integration", () => {
8
- let client;
9
- let mockFetch;
10
-
11
- const MOCK_WIKI_PAGES = {
12
- Fireball: [
13
- "{{skill fact|damage|coefficient=0.9}}",
14
- "{{skill fact|burning|3|stacks=1}}",
15
- "{{skill fact|range|900}}",
16
- ].join("\n"),
17
- Shelter: [
18
- "| split = pve, wvw pvp",
19
- "{{skill fact|healing|4000|coefficient=0.75}}",
20
- "{{skill fact|healing|3200|coefficient=0.6|game mode=wvw pvp}}",
21
- "{{skill fact|recharge|30}}",
22
- ].join("\n"),
23
- "Searing Slash": [
24
- "| split = pve, wvw pvp",
25
- "{{skill fact|damage|coefficient=1.2}}",
26
- "{{skill fact|burning|4|stacks=2|game mode=pve}}",
27
- "{{skill fact|burning|2|stacks=1|game mode=wvw pvp}}",
28
- "{{skill fact|combo|fire}}",
29
- ].join("\n"),
30
- };
31
-
32
- beforeEach(() => {
33
- mockFetch = jest.fn().mockResolvedValue({
34
- ok: true,
35
- json: async () => ({
36
- query: {
37
- pages: Object.fromEntries(
38
- Object.entries(MOCK_WIKI_PAGES).map(([title, wikitext], i) => [
39
- String(i + 1),
40
- { title, revisions: [{ "*": wikitext }] },
41
- ])
42
- ),
43
- },
44
- }),
45
- });
46
- client = new WikiClient({ cache: new MemoryCache(), fetch: mockFetch });
47
- });
48
-
49
- test("resolves all entities with correct per-mode facts", async () => {
50
- const idToTitle = new Map([
51
- [5489, "Fireball"],
52
- [9124, "Shelter"],
53
- [12345, "Searing Slash"],
54
- ]);
55
-
56
- const result = await resolveEntityFacts(client, idToTitle);
57
-
58
- // Fireball: no split
59
- const fireball = result.get(5489);
60
- expect(fireball.hasSplit).toBe(false);
61
- expect(fireball.pve).toHaveLength(3);
62
- expect(fireball.wvw).toBeNull();
63
- expect(fireball.pvp).toBeNull();
64
-
65
- // Shelter: PvE vs WvW/PvP split
66
- const shelter = result.get(9124);
67
- expect(shelter.hasSplit).toBe(true);
68
- expect(shelter.pve.length).toBeGreaterThanOrEqual(2);
69
- expect(shelter.wvw).not.toBeNull();
70
- // PvE healing = 4000, WvW has both universal (4000) and mode-specific (3200)
71
- const pveHeal = shelter.pve.find((f) => f.target === "Healing" || f.text === "Healing");
72
- if (pveHeal) expect(pveHeal.value).toBe(4000);
73
- // WvW mode-specific healing is 3200 (find the one that differs from universal)
74
- const wvwHeals = shelter.wvw.filter((f) => f.target === "Healing" || f.text === "Healing");
75
- expect(wvwHeals.length).toBeGreaterThanOrEqual(1);
76
- const wvwModeHeal = wvwHeals.find((f) => f.value === 3200);
77
- if (wvwModeHeal) expect(wvwModeHeal.value).toBe(3200);
78
-
79
- // Searing Slash: split with different burning stacks
80
- const slash = result.get(12345);
81
- expect(slash.hasSplit).toBe(true);
82
- expect(slash.pve.length).toBeGreaterThanOrEqual(2);
83
- expect(slash.wvw).not.toBeNull();
84
- const pveBurn = slash.pve.find((f) => f.status === "Burning");
85
- const wvwBurn = slash.wvw.find((f) => f.status === "Burning");
86
- if (pveBurn) {
87
- expect(pveBurn.duration).toBe(4);
88
- expect(pveBurn.apply_count).toBe(2);
89
- }
90
- if (wvwBurn) {
91
- expect(wvwBurn.duration).toBe(2);
92
- expect(wvwBurn.apply_count).toBe(1);
93
- }
94
- });
95
-
96
- test("missing wiki pages are not in result map", async () => {
97
- mockFetch.mockReset();
98
- mockFetch.mockResolvedValueOnce({
99
- ok: true,
100
- json: async () => ({
101
- query: {
102
- pages: {
103
- "1": { title: "Fireball", revisions: [{ "*": "{{skill fact|damage|0.8}}" }] },
104
- "-1": { title: "Unknown Skill", missing: true },
105
- },
106
- },
107
- }),
108
- });
109
-
110
- const idToTitle = new Map([
111
- [5489, "Fireball"],
112
- [9999, "Unknown Skill"],
113
- ]);
114
-
115
- const result = await resolveEntityFacts(client, idToTitle);
116
- expect(result.has(5489)).toBe(true);
117
- expect(result.has(9999)).toBe(false);
118
- });
119
-
120
- test("parseFactsByMode correctly separates all three modes", () => {
121
- const wikitext = [
122
- "| split = pve, wvw, pvp",
123
- "{{skill fact|damage|coefficient=1.0}}",
124
- "{{skill fact|recharge|20|game mode=pve}}",
125
- "{{skill fact|recharge|25|game mode=wvw}}",
126
- "{{skill fact|recharge|30|game mode=pvp}}",
127
- ].join("\n");
128
-
129
- const result = parseFactsByMode(wikitext);
130
- expect(result.hasSplit).toBe(true);
131
-
132
- // Damage is universal → all modes
133
- expect(result.pve.length).toBeGreaterThanOrEqual(2);
134
- expect(result.wvw.length).toBeGreaterThanOrEqual(2);
135
- expect(result.pvp.length).toBeGreaterThanOrEqual(2);
136
-
137
- // Recharge differs per mode
138
- const pveRecharge = result.pve.find((f) => f.type === "Recharge");
139
- const wvwRecharge = result.wvw.find((f) => f.type === "Recharge");
140
- const pvpRecharge = result.pvp.find((f) => f.type === "Recharge");
141
- expect(pveRecharge.value).toBe(20);
142
- expect(wvwRecharge.value).toBe(25);
143
- expect(pvpRecharge.value).toBe(30);
144
- });
145
-
146
- test("all fact entries have valid type fields", async () => {
147
- const idToTitle = new Map([
148
- [5489, "Fireball"],
149
- [9124, "Shelter"],
150
- ]);
151
-
152
- const result = await resolveEntityFacts(client, idToTitle);
153
-
154
- for (const [, entity] of result) {
155
- for (const fact of entity.pve) {
156
- expect(fact.type).toBeTruthy();
157
- expect(typeof fact.type).toBe("string");
158
- }
159
- if (entity.wvw) {
160
- for (const fact of entity.wvw) {
161
- expect(fact.type).toBeTruthy();
162
- }
163
- }
164
- }
165
- });
166
-
167
- test("buff facts have status and duration", async () => {
168
- const idToTitle = new Map([[5489, "Fireball"]]);
169
- const result = await resolveEntityFacts(client, idToTitle);
170
- const fireball = result.get(5489);
171
- const buffFacts = fireball.pve.filter((f) => f.type === "Buff");
172
- for (const bf of buffFacts) {
173
- expect(bf.status).toBeTruthy();
174
- expect(typeof bf.duration).toBe("number");
175
- }
176
- });
177
- });
@@ -1,61 +0,0 @@
1
- "use strict";
2
-
3
- const { WikiClient } = require("../src/wiki/client");
4
- const { MemoryCache } = require("../src/wiki/cache");
5
- const { parseFactsByMode } = require("../src/wiki/resolver");
6
-
7
- const RUN_LIVE = process.env.GW2_LIVE_TESTS === "1";
8
-
9
- const REPRESENTATIVE_SKILLS = [
10
- { title: "Fireball", expectedFactTypes: ["Damage"] },
11
- { title: "Shelter", expectedModes: ["pve", "pvp", "wvw"] },
12
- { title: "Signet of Inspiration", minFacts: 1 },
13
- { title: "Shattering Blow", minFacts: 1 },
14
- { title: "Moa Stance", minFacts: 1 },
15
- ];
16
-
17
- (RUN_LIVE ? describe : describe.skip)("Live wiki fact validation", () => {
18
- let client;
19
-
20
- beforeAll(() => {
21
- client = new WikiClient({ cache: new MemoryCache() });
22
- });
23
-
24
- for (const skill of REPRESENTATIVE_SKILLS) {
25
- test(`${skill.title} — parses valid facts from live wiki`, async () => {
26
- const wikitext = await client.getWikitext(skill.title);
27
- expect(wikitext).not.toBeNull();
28
-
29
- const result = parseFactsByMode(wikitext);
30
- expect(result.pve.length).toBeGreaterThanOrEqual(skill.minFacts || 1);
31
-
32
- // Every fact should have a type and text
33
- for (const fact of result.pve) {
34
- expect(fact.type).toBeTruthy();
35
- expect(fact.text).toBeTruthy();
36
- }
37
-
38
- if (skill.expectedFactTypes) {
39
- for (const expectedType of skill.expectedFactTypes) {
40
- expect(result.pve.some((f) => f.type === expectedType)).toBe(true);
41
- }
42
- }
43
-
44
- if (skill.expectedModes) {
45
- for (const mode of skill.expectedModes) {
46
- expect(result[mode].length).toBeGreaterThanOrEqual(1);
47
- }
48
- }
49
- }, 15000);
50
- }
51
-
52
- test("batch fetch works with live wiki", async () => {
53
- const titles = REPRESENTATIVE_SKILLS.map((s) => s.title);
54
- const result = await client.getWikitextBatch(titles);
55
- expect(result.size).toBe(titles.length);
56
- for (const title of titles) {
57
- expect(result.has(title)).toBe(true);
58
- expect(result.get(title)).not.toBeNull();
59
- }
60
- }, 30000);
61
- });
@@ -1,166 +0,0 @@
1
- "use strict";
2
- const { parseFactsByMode } = require("../src/wiki/resolver");
3
-
4
- const SNAPSHOTS = [
5
- {
6
- name: "Fireball — simple damage + burning, no split",
7
- wikitext: [
8
- "{{skill fact|damage|coefficient=0.9|hits=1}}",
9
- "{{skill fact|burning|3|stacks=1}}",
10
- "{{skill fact|targets|5}}",
11
- "{{skill fact|range|900}}",
12
- "{{skill fact|combo|projectile}}",
13
- ].join("\n"),
14
- expected: {
15
- hasSplit: false,
16
- pveCount: 5,
17
- pveTypes: ["Damage", "Buff", "Number", "Range", "ComboFinisher"],
18
- },
19
- },
20
- {
21
- name: "Shelter — healing + block with WvW split",
22
- wikitext: [
23
- "{{skill fact|healing|4000|coefficient=0.75|game mode=pve}}",
24
- "{{skill fact|healing|3200|coefficient=0.75|game mode=wvw pvp}}",
25
- "| split = pve, wvw pvp",
26
- ].join("\n"),
27
- expected: {
28
- hasSplit: true,
29
- pveCount: 1,
30
- wvwCount: 1,
31
- pvpCount: 1,
32
- pveHealingValue: 4000,
33
- wvwHealingValue: 3200,
34
- },
35
- },
36
- {
37
- name: "Signet of Inspiration — boon application",
38
- wikitext: [
39
- "{{skill fact|quickness|2|stacks=1}}",
40
- "{{skill fact|fury|5|stacks=1}}",
41
- "{{skill fact|might|8|stacks=3}}",
42
- "{{skill fact|recharge|30}}",
43
- ].join("\n"),
44
- expected: {
45
- hasSplit: false,
46
- pveCount: 4,
47
- pveTypes: ["Buff", "Buff", "Buff", "Recharge"],
48
- },
49
- },
50
- {
51
- name: "All three mode variants — pve/wvw/pvp burning durations",
52
- wikitext: [
53
- "{{skill fact|damage|coefficient=1.0|hits=1}}",
54
- "{{skill fact|burning|4|stacks=1|game mode=pve}}",
55
- "{{skill fact|burning|2|stacks=1|game mode=wvw}}",
56
- "{{skill fact|burning|1|stacks=1|game mode=pvp}}",
57
- "| split = pve, wvw, pvp",
58
- ].join("\n"),
59
- expected: {
60
- hasSplit: true,
61
- pveCount: 2,
62
- wvwCount: 2,
63
- pvpCount: 2,
64
- pveBurningDuration: 4,
65
- wvwBurningDuration: 2,
66
- pvpBurningDuration: 1,
67
- },
68
- },
69
- {
70
- name: "Combo field skill — Damage, ComboField (fire), Radius",
71
- wikitext: [
72
- "{{skill fact|damage|coefficient=0.5|hits=1}}",
73
- "{{skill fact|combo|fire}}",
74
- "{{skill fact|radius|240}}",
75
- ].join("\n"),
76
- expected: {
77
- hasSplit: false,
78
- pveCount: 3,
79
- pveTypes: ["Damage", "ComboField", "Radius"],
80
- },
81
- },
82
- {
83
- name: "Attribute conversion — BuffConversion + Time",
84
- wikitext: [
85
- "{{skill fact|gain|source=Power|target=Condition Damage|percent=10}}",
86
- "{{skill fact|duration|8}}",
87
- ].join("\n"),
88
- expected: {
89
- hasSplit: false,
90
- pveCount: 2,
91
- pveTypes: ["BuffConversion", "Time"],
92
- },
93
- },
94
- {
95
- name: "Defiance break + conditions removed",
96
- wikitext: [
97
- "{{skill fact|defiance break|200}}",
98
- "{{skill fact|conditions removed|3}}",
99
- "{{skill fact|range|900}}",
100
- ].join("\n"),
101
- expected: {
102
- hasSplit: false,
103
- pveCount: 3,
104
- pveTypes: ["Number", "Number", "Range"],
105
- },
106
- },
107
- ];
108
-
109
- describe("Wiki fact parsing snapshots", () => {
110
- for (const snapshot of SNAPSHOTS) {
111
- test(snapshot.name, () => {
112
- const result = parseFactsByMode(snapshot.wikitext);
113
- const { expected } = snapshot;
114
-
115
- if ("hasSplit" in expected) {
116
- expect(result.hasSplit).toBe(expected.hasSplit);
117
- }
118
-
119
- if ("pveCount" in expected) {
120
- expect(result.pve).toHaveLength(expected.pveCount);
121
- }
122
-
123
- if ("wvwCount" in expected) {
124
- expect(result.wvw).toHaveLength(expected.wvwCount);
125
- }
126
-
127
- if ("pvpCount" in expected) {
128
- expect(result.pvp).toHaveLength(expected.pvpCount);
129
- }
130
-
131
- if ("pveTypes" in expected) {
132
- expect(result.pve.map((f) => f.type)).toEqual(expected.pveTypes);
133
- }
134
-
135
- if ("pveHealingValue" in expected) {
136
- const healingFact = result.pve.find((f) => f.target === "Healing");
137
- expect(healingFact).toBeDefined();
138
- expect(healingFact.value).toBe(expected.pveHealingValue);
139
- }
140
-
141
- if ("wvwHealingValue" in expected) {
142
- const healingFact = result.wvw.find((f) => f.target === "Healing");
143
- expect(healingFact).toBeDefined();
144
- expect(healingFact.value).toBe(expected.wvwHealingValue);
145
- }
146
-
147
- if ("pveBurningDuration" in expected) {
148
- const burningFact = result.pve.find((f) => f.status === "Burning");
149
- expect(burningFact).toBeDefined();
150
- expect(burningFact.duration).toBe(expected.pveBurningDuration);
151
- }
152
-
153
- if ("wvwBurningDuration" in expected) {
154
- const burningFact = result.wvw.find((f) => f.status === "Burning");
155
- expect(burningFact).toBeDefined();
156
- expect(burningFact.duration).toBe(expected.wvwBurningDuration);
157
- }
158
-
159
- if ("pvpBurningDuration" in expected) {
160
- const burningFact = result.pvp.find((f) => f.status === "Burning");
161
- expect(burningFact).toBeDefined();
162
- expect(burningFact.duration).toBe(expected.pvpBurningDuration);
163
- }
164
- });
165
- }
166
- });