fix(inventory): audit base item invfiles and pack authentic charm icons (Fixes #149)
This commit is contained in:
commit
fd2af5353b
Binary file not shown.
|
Before Width: | Height: | Size: 383 KiB After Width: | Height: | Size: 391 KiB |
File diff suppressed because it is too large
Load Diff
|
|
@ -448,20 +448,40 @@ async function main(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
// Register authentic charm DC6 icons (invch1..invch9)
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
const charmInv = `invch${i}`
|
||||
if (archives.has(`data/global/items/${charmInv}.dc6`)) {
|
||||
uniqueInvFiles.add(charmInv)
|
||||
}
|
||||
}
|
||||
|
||||
// Identity mapping for every invfile: codeToInvFile[inv] = inv
|
||||
for (const inv of uniqueInvFiles) {
|
||||
codeToInvFile[inv] = inv
|
||||
}
|
||||
|
||||
// Authentic charm base item code mappings (overriding misc.txt placeholder invfiles invchm, invwnd, invsst)
|
||||
codeToInvFile['cm1'] = 'invch1'
|
||||
codeToInvFile['cm2'] = 'invch4'
|
||||
codeToInvFile['cm3'] = 'invch7'
|
||||
codeToInvFile['gheeds'] = 'invch7'
|
||||
codeToInvFile["Gheed's Fortune"] = 'invch7'
|
||||
|
||||
// Explicitly guarantee Annihilus and Torch mappings
|
||||
if (archives.has('data/global/items/invmss.dc6')) {
|
||||
uniqueInvFiles.add('invmss')
|
||||
codeToInvFile['mss'] = 'invmss'
|
||||
codeToInvFile['invmss'] = 'invmss'
|
||||
codeToInvFile['anni'] = 'invmss'
|
||||
codeToInvFile['Annihilus'] = 'invmss'
|
||||
}
|
||||
if (archives.has('data/global/items/invtrch.dc6')) {
|
||||
uniqueInvFiles.add('invtrch')
|
||||
codeToInvFile['trch'] = 'invtrch'
|
||||
codeToInvFile['invtrch'] = 'invtrch'
|
||||
codeToInvFile['torch'] = 'invtrch'
|
||||
codeToInvFile['Hellfire Torch'] = 'invtrch'
|
||||
}
|
||||
|
||||
// Register transformed item lookups in codeToInvFile
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -122,8 +122,13 @@ export function resolveItemSpriteRect(
|
|||
}
|
||||
}
|
||||
|
||||
// Tier 1: Direct invFile
|
||||
if (item.invFile) {
|
||||
// Tier 1: Direct invFile (ignore misc.txt placeholder invfiles for charms)
|
||||
const isCharmPlaceholder =
|
||||
(item.code === 'cm3' && item.invFile?.toLowerCase() === 'invsst') ||
|
||||
(item.code === 'cm2' && item.invFile?.toLowerCase() === 'invwnd') ||
|
||||
(item.code === 'cm1' && item.invFile?.toLowerCase() === 'invchm')
|
||||
|
||||
if (item.invFile && !isCharmPlaceholder) {
|
||||
if (itemRects[item.invFile]) return itemRects[item.invFile]
|
||||
const lower = item.invFile.toLowerCase()
|
||||
if (itemRects[lower]) return itemRects[lower]
|
||||
|
|
@ -146,13 +151,16 @@ export function resolveItemSpriteRect(
|
|||
if (item.code === 'ibk') return itemRects['invrbk'] ?? null
|
||||
if (item.code === 'box' || item.code === 'cube') return itemRects['invbox'] ?? null
|
||||
if (item.code === 'cm1' || item.code === 'anni' || item.name?.includes('Annihilus')) {
|
||||
return itemRects['invmss'] ?? itemRects['invch1'] ?? itemRects['invchm'] ?? null
|
||||
const direct = item.invFile && item.invFile !== 'invchm' ? (itemRects[item.invFile] ?? itemRects[item.invFile.toLowerCase()]) : undefined
|
||||
return direct ?? itemRects['invch1'] ?? itemRects['invmss'] ?? null
|
||||
}
|
||||
if (item.code === 'cm2' || item.code === 'torch' || item.name?.includes('Torch')) {
|
||||
return itemRects['invtrch'] ?? itemRects['invch2'] ?? null
|
||||
const direct = item.invFile && item.invFile !== 'invwnd' ? (itemRects[item.invFile] ?? itemRects[item.invFile.toLowerCase()]) : undefined
|
||||
return direct ?? itemRects['invch4'] ?? itemRects['invtrch'] ?? null
|
||||
}
|
||||
if (item.code === 'cm3' || item.code === 'gheeds' || item.name?.includes('Gheed')) {
|
||||
return itemRects['invch3'] ?? itemRects['invch6'] ?? itemRects['invch9'] ?? itemRects['invsst'] ?? null
|
||||
const direct = item.invFile && item.invFile !== 'invsst' ? (itemRects[item.invFile] ?? itemRects[item.invFile.toLowerCase()]) : undefined
|
||||
return direct ?? itemRects['invch7'] ?? itemRects['invch8'] ?? itemRects['invch9'] ?? null
|
||||
}
|
||||
if (item.code === 'crs' || item.code === 'cta' || item.name?.includes('Call to Arms')) {
|
||||
return itemRects['invcrs'] ?? null
|
||||
|
|
@ -161,9 +169,15 @@ export function resolveItemSpriteRect(
|
|||
// Tier 5: Dimension-based fallback guarantee (w x h in cells)
|
||||
const w = item.invWidth ?? (item as any).width ?? 1
|
||||
const h = item.invHeight ?? (item as any).height ?? 1
|
||||
if (w === 1 && h === 1) return itemRects['invchm'] ?? itemRects['invrin'] ?? itemRects['invcap'] ?? null
|
||||
if (w === 1 && h === 2) return itemRects['invtrch'] ?? itemRects['invbbk'] ?? itemRects['invglv'] ?? null
|
||||
if (w === 1 && h === 3) return itemRects['invsst'] ?? itemRects['invclb'] ?? itemRects['invwnd'] ?? null
|
||||
if (w === 1 && h === 1) return itemRects['invch1'] ?? itemRects['invchm'] ?? itemRects['invrin'] ?? itemRects['invcap'] ?? null
|
||||
if (w === 1 && h === 2) return itemRects['invch4'] ?? itemRects['invtrch'] ?? itemRects['invbbk'] ?? itemRects['invglv'] ?? null
|
||||
if (w === 1 && h === 3) {
|
||||
const isCharm = item.code?.startsWith('cm') || item.name?.toLowerCase().includes('charm')
|
||||
if (isCharm) {
|
||||
return itemRects['invch7'] ?? itemRects['invch8'] ?? itemRects['invch9'] ?? null
|
||||
}
|
||||
return itemRects['invch7'] ?? itemRects['invsst'] ?? itemRects['invclb'] ?? itemRects['invwnd'] ?? null
|
||||
}
|
||||
if (w === 1 && h === 4) return itemRects['invbst'] ?? itemRects['invstf'] ?? null
|
||||
if (w === 2 && h === 1) return itemRects['invvbl'] ?? itemRects['invlbl'] ?? null
|
||||
if (w === 2 && h === 2) return itemRects['invbox'] ?? itemRects['invcap'] ?? null
|
||||
|
|
@ -526,7 +540,7 @@ export const STARTER_BAG_ITEMS: readonly GridPlacement[] = [
|
|||
item: {
|
||||
id: 'bag-gheeds',
|
||||
code: 'cm3',
|
||||
invFile: 'invch3',
|
||||
invFile: 'invch7',
|
||||
name: "Gheed's Fortune",
|
||||
nameZh: '基德的运气 (Gheed\'s Fortune)',
|
||||
baseNameZh: '超大型护身符 (Grand Charm)',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,279 @@
|
|||
/**
|
||||
* tests/inventory.test.ts
|
||||
*
|
||||
* Comprehensive Unit and Parity Test Suite for Milestone M4 (Issue #149):
|
||||
* Base item `invfile` audit & authentic charm icons (`invch1`..`invch9`).
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { readFileSync, existsSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
resolveItemSpriteRect,
|
||||
STARTER_BAG_ITEMS,
|
||||
STARTER_EQUIPPED_GEAR,
|
||||
InventoryPanel,
|
||||
EQUIP_SLOTS_LAYOUT,
|
||||
INV_GRID_ORIGIN,
|
||||
} from '../src/ui/inventory.ts'
|
||||
import { BAKED_UI_MANIFEST } from '../src/ui/baked-ui-meta.ts'
|
||||
import { MountedArchives } from '../src/mpq/mount.ts'
|
||||
import { MpqArchive } from '../src/mpq/archive.ts'
|
||||
import { fileSource } from '../src/mpq/file-source.ts'
|
||||
import { parseTable } from '../src/game/tables.ts'
|
||||
|
||||
describe('Milestone M4 (Issue #149): Base item invfile audit & charm icons', () => {
|
||||
const itemRects = BAKED_UI_MANIFEST.itemRects
|
||||
const codeToInv = BAKED_UI_MANIFEST.codeToInvFile
|
||||
|
||||
it('bakes all authentic charm DC6 icons invch1..invch9, invmss, invtrch into items atlas', () => {
|
||||
// 1. All 9 charm sprite variants exist in itemRects
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
const charmKey = `invch${i}`
|
||||
expect(itemRects[charmKey], `Expected ${charmKey} in itemRects`).toBeDefined()
|
||||
expect(itemRects[charmKey]!.w).toBe(28)
|
||||
expect(itemRects[charmKey]!.h).toBeGreaterThan(0)
|
||||
}
|
||||
|
||||
// 2. Annihilus and Hellfire Torch icons exist
|
||||
expect(itemRects['invmss']).toBeDefined()
|
||||
expect(itemRects['invmss']!.w).toBe(28)
|
||||
expect(itemRects['invmss']!.h).toBe(28)
|
||||
|
||||
expect(itemRects['invtrch']).toBeDefined()
|
||||
expect(itemRects['invtrch']!.w).toBe(28)
|
||||
expect(itemRects['invtrch']!.h).toBe(56)
|
||||
|
||||
// 3. Confirm authentic DC6 dimensions from 1.13c MPQ
|
||||
expect(itemRects['invch1']!.h).toBe(28) // Small Charm variant 1
|
||||
expect(itemRects['invch2']!.h).toBe(56) // Large Charm variant 1
|
||||
expect(itemRects['invch3']!.h).toBe(84) // Grand Charm variant 1
|
||||
expect(itemRects['invch4']!.h).toBe(28) // Small Charm variant 2
|
||||
expect(itemRects['invch5']!.h).toBe(56) // Large Charm variant 2
|
||||
expect(itemRects['invch6']!.h).toBe(84) // Grand Charm variant 2
|
||||
expect(itemRects['invch7']!.h).toBe(28) // Small Charm variant 3
|
||||
expect(itemRects['invch8']!.h).toBe(56) // Large Charm variant 3
|
||||
expect(itemRects['invch9']!.h).toBe(84) // Grand Charm variant 3
|
||||
})
|
||||
|
||||
it('maps authentic charm item codes to charm DC6 icons in codeToInvFile', () => {
|
||||
// Base charm code mappings
|
||||
expect(codeToInv['cm1']).toBe('invch1')
|
||||
expect(codeToInv['cm2']).toBe('invch4')
|
||||
expect(codeToInv['cm3']).toBe('invch7')
|
||||
|
||||
// Identity mappings for all 9 charm invfiles
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
expect(codeToInv[`invch${i}`]).toBe(`invch${i}`)
|
||||
}
|
||||
|
||||
// Unique charms
|
||||
expect(codeToInv['gheeds']).toBe('invch7')
|
||||
expect(codeToInv["Gheed's Fortune"]).toBe('invch7')
|
||||
expect(codeToInv['anni']).toBe('invmss')
|
||||
expect(codeToInv['Annihilus']).toBe('invmss')
|
||||
expect(codeToInv['torch']).toBe('invtrch')
|
||||
expect(codeToInv['Hellfire Torch']).toBe('invtrch')
|
||||
})
|
||||
|
||||
describe('resolveItemSpriteRect charm resolution', () => {
|
||||
it('cm1 (Small Charm) resolves to 1x1 charm rect (invch1, invch2, invch3, or invmss)', () => {
|
||||
const validSmallCharmKeys = ['invch1', 'invch2', 'invch3', 'invmss']
|
||||
const validSmallCharmRects = validSmallCharmKeys.map((k) => itemRects[k]).filter(Boolean)
|
||||
|
||||
// Base item code cm1
|
||||
const rectBase = resolveItemSpriteRect({ code: 'cm1' }, itemRects)
|
||||
expect(rectBase).not.toBeNull()
|
||||
expect(validSmallCharmRects).toContainEqual(rectBase)
|
||||
expect(rectBase).toEqual(itemRects['invch1'])
|
||||
|
||||
// Item with name
|
||||
const rectName = resolveItemSpriteRect({ code: 'cm1', name: 'Small Charm' }, itemRects)
|
||||
expect(validSmallCharmRects).toContainEqual(rectName)
|
||||
|
||||
// Item with misc.txt placeholder invfile (invchm)
|
||||
const rectPlaceholder = resolveItemSpriteRect(
|
||||
{ code: 'cm1', invFile: 'invchm', name: 'Small Charm of Vita' },
|
||||
itemRects,
|
||||
)
|
||||
expect(rectPlaceholder).not.toBeNull()
|
||||
expect(validSmallCharmRects).toContainEqual(rectPlaceholder)
|
||||
|
||||
// Specific variants invch2, invch3
|
||||
const rectVar2 = resolveItemSpriteRect({ code: 'cm1', invFile: 'invch2' }, itemRects)
|
||||
expect(rectVar2).toEqual(itemRects['invch2'])
|
||||
|
||||
const rectVar3 = resolveItemSpriteRect({ code: 'cm1', invFile: 'invch3' }, itemRects)
|
||||
expect(rectVar3).toEqual(itemRects['invch3'])
|
||||
|
||||
// Unique Annihilus
|
||||
const rectAnni = resolveItemSpriteRect({ code: 'anni', name: 'Annihilus' }, itemRects)
|
||||
expect(rectAnni).toEqual(itemRects['invmss'])
|
||||
})
|
||||
|
||||
it('cm2 (Large Charm) resolves to 1x2 charm rect (invch4, invch5, invch6, or invtrch)', () => {
|
||||
const validLargeCharmKeys = ['invch4', 'invch5', 'invch6', 'invtrch']
|
||||
const validLargeCharmRects = validLargeCharmKeys.map((k) => itemRects[k]).filter(Boolean)
|
||||
|
||||
// Base item code cm2
|
||||
const rectBase = resolveItemSpriteRect({ code: 'cm2' }, itemRects)
|
||||
expect(rectBase).not.toBeNull()
|
||||
expect(validLargeCharmRects).toContainEqual(rectBase)
|
||||
expect(rectBase).toEqual(itemRects['invch4'])
|
||||
|
||||
// Item with misc.txt placeholder invfile (invwnd - Wand)
|
||||
const rectPlaceholder = resolveItemSpriteRect(
|
||||
{ code: 'cm2', invFile: 'invwnd', name: 'Large Charm of Strength' },
|
||||
itemRects,
|
||||
)
|
||||
expect(rectPlaceholder).not.toBeNull()
|
||||
expect(validLargeCharmRects).toContainEqual(rectPlaceholder)
|
||||
expect(rectPlaceholder).not.toEqual(itemRects['invwnd'])
|
||||
|
||||
// Specific variants invch5, invch6
|
||||
const rectVar5 = resolveItemSpriteRect({ code: 'cm2', invFile: 'invch5' }, itemRects)
|
||||
expect(rectVar5).toEqual(itemRects['invch5'])
|
||||
|
||||
const rectVar6 = resolveItemSpriteRect({ code: 'cm2', invFile: 'invch6' }, itemRects)
|
||||
expect(rectVar6).toEqual(itemRects['invch6'])
|
||||
|
||||
// Unique Hellfire Torch
|
||||
const rectTorch = resolveItemSpriteRect({ code: 'torch', name: 'Hellfire Torch' }, itemRects)
|
||||
expect(rectTorch).toEqual(itemRects['invtrch'])
|
||||
})
|
||||
|
||||
it('cm3 (Grand Charm / Gheed) resolves to 1x3 charm rect (invch7, invch8, invch9), and NOT invsst', () => {
|
||||
const validGrandCharmKeys = ['invch7', 'invch8', 'invch9']
|
||||
const validGrandCharmRects = validGrandCharmKeys.map((k) => itemRects[k]).filter(Boolean)
|
||||
|
||||
// Base item code cm3
|
||||
const rectBase = resolveItemSpriteRect({ code: 'cm3' }, itemRects)
|
||||
expect(rectBase).not.toBeNull()
|
||||
expect(validGrandCharmRects).toContainEqual(rectBase)
|
||||
expect(rectBase).toEqual(itemRects['invch7'])
|
||||
expect(rectBase).not.toEqual(itemRects['invsst'])
|
||||
|
||||
// Item with misc.txt placeholder invfile (invsst - Short Staff)
|
||||
const rectPlaceholder = resolveItemSpriteRect(
|
||||
{ code: 'cm3', invFile: 'invsst', name: 'Grand Charm of Balance' },
|
||||
itemRects,
|
||||
)
|
||||
expect(rectPlaceholder).not.toBeNull()
|
||||
expect(validGrandCharmRects).toContainEqual(rectPlaceholder)
|
||||
expect(rectPlaceholder).not.toEqual(itemRects['invsst'])
|
||||
|
||||
// Specific variants invch8, invch9
|
||||
const rectVar8 = resolveItemSpriteRect({ code: 'cm3', invFile: 'invch8' }, itemRects)
|
||||
expect(rectVar8).toEqual(itemRects['invch8'])
|
||||
|
||||
const rectVar9 = resolveItemSpriteRect({ code: 'cm3', invFile: 'invch9' }, itemRects)
|
||||
expect(rectVar9).toEqual(itemRects['invch9'])
|
||||
|
||||
// Unique Gheed's Fortune
|
||||
const rectGheeds = resolveItemSpriteRect({ code: 'cm3', name: "Gheed's Fortune" }, itemRects)
|
||||
expect(rectGheeds).not.toBeNull()
|
||||
expect(validGrandCharmRects).toContainEqual(rectGheeds)
|
||||
expect(rectGheeds).toEqual(itemRects['invch7'])
|
||||
expect(rectGheeds).not.toEqual(itemRects['invsst'])
|
||||
|
||||
const rectGheedsAlias = resolveItemSpriteRect({ code: 'gheeds' }, itemRects)
|
||||
expect(rectGheedsAlias).not.toBeNull()
|
||||
expect(validGrandCharmRects).toContainEqual(rectGheedsAlias)
|
||||
expect(rectGheedsAlias).not.toEqual(itemRects['invsst'])
|
||||
})
|
||||
|
||||
it('enforces Tier 5 dimension fallback for 1x3 items to avoid rendering as wooden staff', () => {
|
||||
// 1x3 item with charm name or cm code
|
||||
const fallbackCharm = { name: 'Unknown Grand Charm', invWidth: 1, invHeight: 3 }
|
||||
const rectCharm = resolveItemSpriteRect(fallbackCharm, itemRects)
|
||||
expect(rectCharm).toEqual(itemRects['invch7'])
|
||||
expect(rectCharm).not.toEqual(itemRects['invsst'])
|
||||
|
||||
// 1x3 item with cm3 code and missing invfile
|
||||
const fallbackCm3 = { code: 'cm3', invWidth: 1, invHeight: 3 }
|
||||
const rectCm3 = resolveItemSpriteRect(fallbackCm3, itemRects)
|
||||
expect(rectCm3).toEqual(itemRects['invch7'])
|
||||
expect(rectCm3).not.toEqual(itemRects['invsst'])
|
||||
|
||||
// Generic 1x3 item fallback
|
||||
const generic1x3 = { invWidth: 1, invHeight: 3 }
|
||||
const rect1x3 = resolveItemSpriteRect(generic1x3, itemRects)
|
||||
expect(rect1x3).not.toBeNull()
|
||||
expect(rect1x3).toEqual(itemRects['invch7'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('Comprehensive Base Item & Starter Inventory Audit', () => {
|
||||
it('resolves valid non-null sprite rectangles for all codes in codeToInvFile', () => {
|
||||
let resolvedCount = 0
|
||||
for (const [code, invFile] of Object.entries(codeToInv)) {
|
||||
const rect = resolveItemSpriteRect({ code, invFile }, itemRects)
|
||||
expect(rect, `Item code "${code}" (invFile "${invFile}") must resolve to sprite rect`).not.toBeNull()
|
||||
expect(rect!.w, `Item code "${code}" width must be > 0`).toBeGreaterThan(0)
|
||||
expect(rect!.h, `Item code "${code}" height must be > 0`).toBeGreaterThan(0)
|
||||
resolvedCount++
|
||||
}
|
||||
expect(resolvedCount).toBeGreaterThanOrEqual(300)
|
||||
})
|
||||
|
||||
it('resolves valid sprite rectangles for all starter equipped items and bag items', () => {
|
||||
for (const [slot, item] of Object.entries(STARTER_EQUIPPED_GEAR)) {
|
||||
if (!item) continue
|
||||
const rect = resolveItemSpriteRect(item, itemRects)
|
||||
expect(rect, `Starter equipped ${slot} (${item.name}) must resolve`).not.toBeNull()
|
||||
expect(rect!.w).toBeGreaterThan(0)
|
||||
expect(rect!.h).toBeGreaterThan(0)
|
||||
}
|
||||
|
||||
for (const { item, col, row } of STARTER_BAG_ITEMS) {
|
||||
const rect = resolveItemSpriteRect(item, itemRects)
|
||||
expect(rect, `Starter bag item at (${col}, ${row}) (${item.name}) must resolve`).not.toBeNull()
|
||||
expect(rect!.w).toBeGreaterThan(0)
|
||||
expect(rect!.h).toBeGreaterThan(0)
|
||||
|
||||
// Specifically check charms in bag
|
||||
if (item.id === 'bag-gheeds') {
|
||||
expect(rect).toEqual(itemRects['invch7'])
|
||||
expect(rect).not.toEqual(itemRects['invsst'])
|
||||
} else if (item.id === 'bag-torch') {
|
||||
expect(rect).toEqual(itemRects['invtrch'])
|
||||
} else if (item.id === 'bag-anni') {
|
||||
expect(rect).toEqual(itemRects['invmss'])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('audits all base items from authentic 1.13c misc.txt, weapons.txt, and armor.txt if MPQs present', async () => {
|
||||
const d2Path = join(process.cwd(), 'samples', 'd2')
|
||||
if (!existsSync(join(d2Path, 'Patch_D2.mpq'))) {
|
||||
return // Skip MPQ read if samples/d2 is not mounted in environment
|
||||
}
|
||||
|
||||
const archives = new MountedArchives()
|
||||
for (const m of ['d2char.mpq', 'd2data.mpq', 'd2exp.mpq', 'Patch_D2.mpq']) {
|
||||
archives.add(m, await MpqArchive.open(await fileSource(join(d2Path, m))))
|
||||
}
|
||||
|
||||
for (const tblFile of ['weapons.txt', 'armor.txt', 'misc.txt']) {
|
||||
const data = await archives.read(`data/global/excel/${tblFile}`)
|
||||
const tbl = parseTable(new TextDecoder().decode(data))
|
||||
for (const row of tbl.rows) {
|
||||
const code = row['code']?.trim()
|
||||
if (!code) continue
|
||||
const invfile = row['invfile']?.trim()
|
||||
const rect = resolveItemSpriteRect({ code, invFile: invfile, name: row['name'] }, itemRects)
|
||||
expect(rect, `Base item ${code} (${row['name']}) from ${tblFile} must resolve to sprite rect`).not.toBeNull()
|
||||
expect(rect!.w).toBeGreaterThan(0)
|
||||
expect(rect!.h).toBeGreaterThan(0)
|
||||
|
||||
// Verify charm items never resolve to staff or wand
|
||||
if (code === 'cm3') {
|
||||
expect(rect).not.toEqual(itemRects['invsst'])
|
||||
} else if (code === 'cm2') {
|
||||
expect(rect).not.toEqual(itemRects['invwnd'])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue