@axiapps/gw2-data 0.1.1 → 0.1.2

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 (42) hide show
  1. package/data/overrides.json +24 -0
  2. package/package.json +3 -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/wiki/parser.js +17 -9
  9. package/scripts/generate-fixtures.js +0 -242
  10. package/tests/api-client.test.js +0 -138
  11. package/tests/cache.test.js +0 -108
  12. package/tests/engine/attributes.test.js +0 -252
  13. package/tests/engine/boons.test.js +0 -129
  14. package/tests/engine/combos.test.js +0 -76
  15. package/tests/engine/constants.test.js +0 -576
  16. package/tests/engine/fixtures/berserker-thief.json +0 -61
  17. package/tests/engine/fixtures/berserker-warrior.json +0 -113
  18. package/tests/engine/fixtures/celestial-firebrand-wvw.json +0 -94
  19. package/tests/engine/fixtures/harrier-druid.json +0 -119
  20. package/tests/engine/fixtures/viper-mirage.json +0 -104
  21. package/tests/engine/graph.test.js +0 -30
  22. package/tests/engine/integration.test.js +0 -111
  23. package/tests/engine/modifiers.test.js +0 -473
  24. package/tests/engine/overrides.test.js +0 -70
  25. package/tests/engine/snapshot.test.js +0 -53
  26. package/tests/engine/test-utils.js +0 -20
  27. package/tests/engine/tooltips.test.js +0 -62
  28. package/tests/fixtures/capture.js +0 -160
  29. package/tests/fixtures/fixtures.json +0 -839
  30. package/tests/integration.test.js +0 -100
  31. package/tests/match.test.js +0 -176
  32. package/tests/merge.test.js +0 -128
  33. package/tests/normalize.test.js +0 -78
  34. package/tests/parser.test.js +0 -506
  35. package/tests/real-data.test.js +0 -296
  36. package/tests/relations.test.js +0 -80
  37. package/tests/resolver.test.js +0 -721
  38. package/tests/validate-live.js +0 -191
  39. package/tests/wiki-client.test.js +0 -468
  40. package/tests/wiki-integration.test.js +0 -177
  41. package/tests/wiki-live-validation.test.js +0 -61
  42. 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
- });