From 7712abe3f079b2fd71667cc337e1e46cecdf95c5 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Mon, 27 Apr 2026 13:31:18 +0200 Subject: [PATCH] fix: fix parsing of mercurial in dragon-item-parser --- dragon-item-parser/src/item.ts | 12 ++++--- dragon-item-parser/test/item.test.ts | 49 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/dragon-item-parser/src/item.ts b/dragon-item-parser/src/item.ts index 1c13199..d9f1b5f 100644 --- a/dragon-item-parser/src/item.ts +++ b/dragon-item-parser/src/item.ts @@ -544,11 +544,15 @@ function parseEffects(description: string): ItemEffect[] { const activeMatch = line.match(/^([^<]*)<\/active>(.*)$/i) if (activeMatch) { const remainingContent = activeMatch[2].trim() - // Skip lines that are just "ACTIVE" labels (like "(0s)" cooldown indicators) const effectName = activeMatch[1].trim() - if (effectName.toUpperCase() === 'ACTIVE' && remainingContent.match(/^\([^)]*\)\s*$/)) { - // This is just a cooldown label, skip it - continue + + // Skip lines that are just "ACTIVE" labels + // This includes: standalone "ACTIVE", or "ACTIVE" followed by cooldown like "(0s)" + if (effectName.toUpperCase() === 'ACTIVE') { + // Skip if it's just "ACTIVE" with no other content, or just a cooldown indicator + if (!remainingContent || remainingContent.match(/^\([^)]*\)\s*$/)) { + continue + } } if (!remainingContent) { diff --git a/dragon-item-parser/test/item.test.ts b/dragon-item-parser/test/item.test.ts index 393ba58..b3695c0 100644 --- a/dragon-item-parser/test/item.test.ts +++ b/dragon-item-parser/test/item.test.ts @@ -441,3 +441,52 @@ describe('parseItemFull', () => { expect(result.parsedDescription.effects[0].name).toBe('Magical Opus') }) }) + +describe('edge case items', () => { + it('should parse Mercurial Scimitar with ACTIVE label before effect name', () => { + const description = + ' 50 Attack Damage
35 Magic Resist
10% Life Steal




ACTIVE
Quicksilver
Removes all crowd control debuffs (excluding Airborne) and grants Move Speed.
' + + const result = parseItemDescription(description) + expect(result.stats.attackDamage).toBe(50) + expect(result.stats.magicResist).toBe(35) + expect(result.stats.lifeSteal).toBe(10) + expect(result.effects).toHaveLength(1) + expect(result.effects[0].type).toBe('active') + expect(result.effects[0].name).toBe('Quicksilver') + expect(result.effects[0].description.length).toBeGreaterThan(0) + expect(result.effects[0].description[0].content).toContain('Removes all crowd control') + }) + + it('should parse Hextech Gunblade with ACTIVE label and cooldown', () => { + const description = + ' 80 Ability Power
40 Attack Damage
10% Omnivamp




ACTIVE (0s)
Lightning Bolt
Shocks the target enemy champion, dealing magic damage and slowing them by 25% for 1.5 seconds.
' + + const result = parseItemDescription(description) + expect(result.stats.abilityPower).toBe(80) + expect(result.stats.attackDamage).toBe(40) + expect(result.stats.omnivamp).toBe(10) + expect(result.effects).toHaveLength(1) + expect(result.effects[0].type).toBe('active') + expect(result.effects[0].name).toBe('Lightning Bolt') + expect(result.effects[0].description.length).toBeGreaterThan(0) + expect(result.effects[0].description[0].content).toContain('Shocks the target enemy champion') + }) + + it('should parse Dark Seal with passive name appearing in description', () => { + const description = + ' 15 Ability Power
50 Health


Glory
Takedowns grant Glory, up to 10. 5 Glory is lost on death.
Gain 4 Ability Power per Glory.
' + + const result = parseItemDescription(description) + expect(result.stats.abilityPower).toBe(15) + expect(result.stats.health).toBe(50) + expect(result.effects).toHaveLength(1) + expect(result.effects[0].type).toBe('passive') + expect(result.effects[0].name).toBe('Glory') + // Description should contain the full text with Glory mentions + const descText = result.effects[0].description.map(d => d.content).join('') + expect(descText).toContain('Takedowns') + expect(descText).toContain('Glory') + expect(descText).toContain('Ability Power') + }) +})