import { describe, it, expect } from 'vitest' import { parseItemStats, parseItem, parseItems, parseItemDescription, parseItemFull } from '../src/item.js' describe('parseItemStats', () => { describe('basic stats', () => { it('should parse Move Speed', () => { // Boots (id: 1001) const description = ' 25 Move Speed

' const stats = parseItemStats(description) expect(stats.moveSpeed).toBe(25) }) it('should parse Attack Damage', () => { // Long Sword (id: 1036) const description = ' 10 Attack Damage

' const stats = parseItemStats(description) expect(stats.attackDamage).toBe(10) }) it('should parse Ability Power', () => { // Amplifying Tome (id: 1052) const description = ' 20 Ability Power

' const stats = parseItemStats(description) expect(stats.abilityPower).toBe(20) }) it('should parse Health', () => { // Ruby Crystal (id: 1028) const description = ' 150 Health

' const stats = parseItemStats(description) expect(stats.health).toBe(150) }) it('should parse Armor', () => { // Cloth Armor (id: 1029) const description = ' 15 Armor

' const stats = parseItemStats(description) expect(stats.armor).toBe(15) }) it('should parse Magic Resist', () => { // Null-Magic Mantle (id: 1033) const description = ' 20 Magic Resist

' const stats = parseItemStats(description) expect(stats.magicResist).toBe(20) }) it('should parse Mana', () => { // Sapphire Crystal (id: 1027) const description = ' 300 Mana

' const stats = parseItemStats(description) expect(stats.mana).toBe(300) }) }) describe('percentage stats', () => { it('should parse Attack Speed percentage', () => { // Dagger (id: 1042) const description = ' 10% Attack Speed

' const stats = parseItemStats(description) expect(stats.attackSpeed).toBe(10) }) it('should parse Critical Strike Chance', () => { // Cloak of Agility (id: 1018) const description = ' 15% Critical Strike Chance

' const stats = parseItemStats(description) expect(stats.criticalStrikeChance).toBe(15) }) it('should parse Life Steal', () => { // Vampiric Scepter (id: 1053) const description = ' 15 Attack Damage
7% Life Steal


' const stats = parseItemStats(description) expect(stats.attackDamage).toBe(15) expect(stats.lifeSteal).toBe(7) }) it('should parse Base Mana Regen percentage', () => { // Faerie Charm (id: 1004) const description = ' 50% Base Mana Regen

' const stats = parseItemStats(description) expect(stats.baseManaRegen).toBe(50) }) it('should parse Base Health Regen percentage', () => { // Rejuvenation Bead (id: 1006) const description = ' 100% Base Health Regen

' const stats = parseItemStats(description) expect(stats.baseHealthRegen).toBe(100) }) }) describe('multiple stats', () => { it("should parse multiple stats from Doran's Blade", () => { // Doran's Blade (id: 1055) const description = ' 10 Attack Damage
80 Health
2.5% Omnivamp


' const stats = parseItemStats(description) expect(stats.attackDamage).toBe(10) expect(stats.health).toBe(80) expect(stats.omnivamp).toBe(2.5) }) it("should parse multiple stats from Doran's Ring", () => { // Doran's Ring (id: 1056) const description = ' 18 Ability Power
90 Health


' const stats = parseItemStats(description) expect(stats.abilityPower).toBe(18) expect(stats.health).toBe(90) }) it("should parse multiple stats from Seeker's Armguard", () => { // Seeker's Armguard (id: 2420) const description = ' 40 Ability Power
25 Armor


' const stats = parseItemStats(description) expect(stats.abilityPower).toBe(40) expect(stats.armor).toBe(25) }) it('should parse complex item with many stats', () => { // Overlord's Bloodmail (id: 2501) const description = ' 30 Attack Damage
550 Health


' const stats = parseItemStats(description) expect(stats.attackDamage).toBe(30) expect(stats.health).toBe(550) }) it('should parse item with Ability Haste', () => { // Unending Despair (id: 2502) const description = ' 400 Health
50 Armor
15 Ability Haste


' const stats = parseItemStats(description) expect(stats.health).toBe(400) expect(stats.armor).toBe(50) expect(stats.abilityHaste).toBe(15) }) it('should parse item with Mana and Ability Haste', () => { // Blackfire Torch (id: 2503) const description = ' 80 Ability Power
600 Mana
20 Ability Haste


' const stats = parseItemStats(description) expect(stats.abilityPower).toBe(80) expect(stats.mana).toBe(600) expect(stats.abilityHaste).toBe(20) }) }) describe('edge cases', () => { it('should return empty object for empty description', () => { const stats = parseItemStats('') expect(stats).toEqual({}) }) it('should return empty object for description without stats', () => { const description = '

' const stats = parseItemStats(description) expect(stats).toEqual({}) }) it('should handle description with only passive text', () => { // Emberknife (id: 1035) - no stats, only passives const description = '

7% Omnivamp against jungle monsters
  • Sear: Damaging jungle monsters burns them for magic damage over 5 seconds.' const stats = parseItemStats(description) expect(stats).toEqual({}) }) it('should handle decimal values', () => { const description = ' 2.5% Omnivamp

    ' const stats = parseItemStats(description) expect(stats.omnivamp).toBe(2.5) }) }) }) describe('parseItemDescription', () => { describe('stats parsing', () => { it('should parse stats from description', () => { const description = ' 75 Attack Damage
    25% Critical Strike Chance


    ' const parsed = parseItemDescription(description) expect(parsed.stats.attackDamage).toBe(75) expect(parsed.stats.criticalStrikeChance).toBe(25) }) it('should parse Infinity Edge stats', () => { // Infinity Edge (id: 3031) const description = ' 75 Attack Damage
    25% Critical Strike Chance
    30% Critical Strike Damage


    ' const parsed = parseItemDescription(description) expect(parsed.stats.attackDamage).toBe(75) expect(parsed.stats.criticalStrikeChance).toBe(25) // Critical Strike Damage is not in our mappings, but should not crash }) }) describe('effects parsing', () => { it('should parse passive effect', () => { // Rabadon's Deathcap (id: 3089) const description = ' 130 Ability Power

    Magical Opus
    Increases your total Ability Power by 30%.
    ' const parsed = parseItemDescription(description) expect(parsed.effects).toHaveLength(1) expect(parsed.effects[0].type).toBe('passive') expect(parsed.effects[0].name).toBe('Magical Opus') }) it('should parse active effect', () => { // Zhonya's Hourglass (id: 3157) const description = ' 105 Ability Power
    50 Armor




    Time Stop
    Enter Stasis for 2.5 seconds.
    ' const parsed = parseItemDescription(description) expect(parsed.effects).toHaveLength(1) expect(parsed.effects[0].type).toBe('active') expect(parsed.effects[0].name).toBe('Time Stop') }) it('should parse multiple passives', () => { // Trinity Force (id: 3078) const description = ' 36 Attack Damage
    30% Attack Speed
    333 Health
    15 Ability Haste


    Spellblade
    After using an Ability, your next Attack deals bonus physical damage On-Hit.

    Quicken
    Attacking grants 20 Move Speed for 2 seconds.
    ' const parsed = parseItemDescription(description) expect(parsed.effects).toHaveLength(2) expect(parsed.effects[0].type).toBe('passive') expect(parsed.effects[0].name).toBe('Spellblade') expect(parsed.effects[1].type).toBe('passive') expect(parsed.effects[1].name).toBe('Quicken') }) it('should parse passive with status tag', () => { // Serylda's Grudge (id: 6694) const description = ' 45 Attack Damage
    35% Armor Penetration
    15 Ability Haste


    Bitter Cold
    Damaging Abilities Slow enemies below 50% Health by 30% for 1 second.
    ' const parsed = parseItemDescription(description) expect(parsed.effects).toHaveLength(1) expect(parsed.effects[0].type).toBe('passive') expect(parsed.effects[0].name).toBe('Bitter Cold') }) it('should handle item with no effects', () => { // Infinity Edge has no passive/active tags const description = ' 75 Attack Damage
    25% Critical Strike Chance
    30% Critical Strike Damage


    ' const parsed = parseItemDescription(description) expect(parsed.effects).toHaveLength(0) }) }) describe('text segments', () => { it('should parse scaleAP tag', () => { const description = ' 130 Ability Power

    Magical Opus
    Increases your total Ability Power by 30%.
    ' const parsed = parseItemDescription(description) // Find the scaleAP segment const effect = parsed.effects[0] const scaleSegment = effect.description.find(s => s.type === 'scaleAP') expect(scaleSegment).toBeDefined() expect(scaleSegment?.content).toContain('Ability Power by 30%') }) it('should parse physicalDamage tag', () => { const description = ' 36 Attack Damage

    Spellblade
    Deals bonus physical damage.
    ' const parsed = parseItemDescription(description) const effect = parsed.effects[0] const damageSegment = effect.description.find(s => s.type === 'physicalDamage') expect(damageSegment).toBeDefined() expect(damageSegment?.content).toContain('bonus physical damage') }) it('should parse keyword tag', () => { const description = ' 105 Ability Power

    Time Stop
    Enter Stasis for 2.5 seconds.
    ' const parsed = parseItemDescription(description) const effect = parsed.effects[0] const keywordSegment = effect.description.find(s => s.type === 'keyword') expect(keywordSegment).toBeDefined() expect(keywordSegment?.content).toBe('Stasis') }) it('should parse speed tag', () => { const description = ' 36 Attack Damage

    Quicken
    Grants 20 Move Speed.
    ' const parsed = parseItemDescription(description) const effect = parsed.effects[0] const speedSegment = effect.description.find(s => s.type === 'speed') expect(speedSegment).toBeDefined() expect(speedSegment?.content).toContain('20 Move Speed') }) it('should parse status tag', () => { const description = ' 45 Attack Damage

    Bitter Cold
    Slow enemies.
    ' const parsed = parseItemDescription(description) const effect = parsed.effects[0] const statusSegment = effect.description.find(s => s.type === 'status') expect(statusSegment).toBeDefined() expect(statusSegment?.content).toBe('Slow') }) }) describe('rarity detection', () => { it('should detect mythic rarity', () => { const description = ' 65 Attack Damage

    Mythic
    ' const parsed = parseItemDescription(description) expect(parsed.rarity).toBe('mythic') }) it('should detect legendary rarity', () => { const description = ' 65 Attack Damage

    Legendary
    ' const parsed = parseItemDescription(description) expect(parsed.rarity).toBe('legendary') }) it('should detect epic rarity', () => { const description = ' 65 Attack Damage

    Epic
    ' const parsed = parseItemDescription(description) expect(parsed.rarity).toBe('epic') }) }) describe('rules and flavor text', () => { it('should parse rules section', () => { const description = ' 25 Move Speed

    Ornn upgrade only
    ' const parsed = parseItemDescription(description) expect(parsed.rules).toBeDefined() expect(parsed.rules?.length).toBeGreaterThan(0) }) it('should parse flavor text section', () => { const description = ' 25 Move Speed

    A ancient artifact
    ' const parsed = parseItemDescription(description) expect(parsed.flavorText).toBeDefined() expect(parsed.flavorText?.length).toBeGreaterThan(0) }) }) }) describe('parseItem', () => { it('should parse item and add stats', () => { const item = { id: 1001, name: 'Boots', description: ' 25 Move Speed

    ', iconPath: '/path/to/icon.png' } const result = parseItem(item) expect(result.id).toBe(1001) expect(result.name).toBe('Boots') expect(result.stats.moveSpeed).toBe(25) }) }) describe('parseItems', () => { it('should parse multiple items', () => { const items = [ { id: 1001, name: 'Boots', description: ' 25 Move Speed

    ', iconPath: '/path/to/boots.png' }, { id: 1036, name: 'Long Sword', description: ' 10 Attack Damage

    ', iconPath: '/path/to/sword.png' } ] const results = parseItems(items) expect(results).toHaveLength(2) expect(results[0].stats.moveSpeed).toBe(25) expect(results[1].stats.attackDamage).toBe(10) }) }) describe('parseItemFull', () => { it('should parse item with full description', () => { const item = { id: 3089, name: "Rabadon's Deathcap", description: ' 130 Ability Power

    Magical Opus
    Increases your total Ability Power by 30%.
    ', iconPath: '/path/to/icon.png' } const result = parseItemFull(item) expect(result.id).toBe(3089) expect(result.name).toBe("Rabadon's Deathcap") expect(result.parsedDescription.stats.abilityPower).toBe(130) expect(result.parsedDescription.effects).toHaveLength(1) expect(result.parsedDescription.effects[0].name).toBe('Magical Opus') }) })