From 5738bfe4638fa196a44b7e529576f5dd6f8cb13d Mon Sep 17 00:00:00 2001 From: Mickael Cagnion Date: Wed, 5 Aug 2026 23:29:17 +0200 Subject: [PATCH 1/2] Preserve punctuation in Buy Similar unique names Keep official unique names intact while stripping user-added trailing numeric IDs. --- spec/System/TestCompareBuySimilar_spec.lua | 46 ++++++++++++++++++++++ src/Classes/CompareBuySimilar.lua | 12 +++--- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/spec/System/TestCompareBuySimilar_spec.lua b/spec/System/TestCompareBuySimilar_spec.lua index 15f814310ef..ab283d5d205 100644 --- a/spec/System/TestCompareBuySimilar_spec.lua +++ b/spec/System/TestCompareBuySimilar_spec.lua @@ -154,6 +154,16 @@ Implicits: 1 return dkjson.decode(queryJson) end + local function getUniqueQuery(name, baseName) + local url = bs.buildURL({ title = name, baseName = baseName }, nil, { + realmDrop = { GetSelValue = function() return "PC" end }, + leagueDrop = { GetSelValue = function() return "Standard" end }, + listedDrop = { selIndex = 1 }, + }, {}, {}, true) + local queryJson = urlDecode(assert(url:match("[?&]q=(.+)$"))) + return dkjson.decode(queryJson) + end + it("rebuilds the URL when league and listed status change", function() local controls = openPopup() getQuery(controls) @@ -171,6 +181,42 @@ Implicits: 1 assert.equal("any", query.query.status.option) end) + it("preserves apostrophes in unique names", function() + local item = new("Item", [[ +Rarity: UNIQUE +Ralakesh's Impatience +Riveted Boots +Implicits: 0]]) + local controls = openPopup(item, "Boots") + + assert.equal("Ralakesh's Impatience", getQuery(controls).query.name) + end) + + it("preserves every loaded unique name in direct URLs", function() + local expectedNames = { + ["The Hateful Accuser "] = "The Hateful Accuser", + } + local uniqueCount = 0 + for _, typeList in pairs(data.uniques) do + for _, uniqueText in ipairs(typeList) do + local name, baseName = uniqueText:match("^([^\n]+)\n([^\n]+)") + assert.is_not_nil(name) + assert.is_not_nil(baseName) + assert.equal(expectedNames[name] or name, getUniqueQuery(name, baseName).query.name) + uniqueCount = uniqueCount + 1 + end + end + assert.is_true(uniqueCount > 0) + end) + + it("removes a trailing unique ID without removing name punctuation", function() + assert.equal("Uul-Netol's Embrace", getUniqueQuery("Uul-Netol's Embrace 1234", "Vaal Axe").query.name) + end) + + it("trims whitespace after a unique name", function() + assert.equal("The Hateful Accuser", getUniqueQuery("The Hateful Accuser ", "Ghastly Eye Jewel").query.name) + end) + it("persists league choices by name for each realm", function() local controls = openPopup() assert.equal("PC Event", controls.leagueDrop:GetSelValue()) diff --git a/src/Classes/CompareBuySimilar.lua b/src/Classes/CompareBuySimilar.lua index ede83e401fa..f0f7a8cb67a 100644 --- a/src/Classes/CompareBuySimilar.lua +++ b/src/Classes/CompareBuySimilar.lua @@ -41,7 +41,7 @@ end -- Build the trade search URL based on popup selections -local function buildURL(item, slotName, controls, modEntries, defenceEntries, isUnique) +function M.buildURL(item, slotName, controls, modEntries, defenceEntries, isUnique) -- Determine realm and league from the popup's dropdowns local realmDisplayValue = controls.realmDrop and controls.realmDrop:GetSelValue() or "PC" local realm = REALM_API_IDS[realmDisplayValue] or "pc" @@ -74,10 +74,10 @@ local function buildURL(item, slotName, controls, modEntries, defenceEntries, is -- Search by unique name -- Strip "Foulborn" prefix from unique name for trade search local tradeName = (item.title or item.name):gsub("^Foulborn%s+", "") - -- only take the first letters and white space to avoid e.g. including - -- timeless jewel ids or other numbers the user might have on the item - local nameMatch = tradeName:match("(%a[%a%s]+).*") - tradeName = (nameMatch and nameMatch:gsub("%s+$", "")) or tradeName + -- only strip a trailing numeric identifier to avoid e.g. including + -- timeless jewel ids or other numbers appended to the item name. + tradeName = tradeName:gsub("%s+$", "") + tradeName = tradeName:match("^(.-)%s+%d+$") or tradeName queryTable.query.name = tradeName queryTable.query.type = item.baseName -- If item is Foulborn, add the foulborn_item filter @@ -339,7 +339,7 @@ function M.openPopup(item, slotName, primaryBuild) end local function rebuildUrl() - local result = buildURL(item, slotName, controls, modEntries, defenceEntries, isUnique) + local result = M.buildURL(item, slotName, controls, modEntries, defenceEntries, isUnique) uri = result end -- Helper to fetch and populate leagues for a given realm API id From d12e8d5a4c0de15f0e3b74f297866e8ba41bbcc0 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Thu, 6 Aug 2026 12:47:50 +1000 Subject: [PATCH 2/2] Retain local function --- spec/System/TestCompareBuySimilar_spec.lua | 28 ++++------------------ src/Classes/CompareBuySimilar.lua | 4 ++-- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/spec/System/TestCompareBuySimilar_spec.lua b/spec/System/TestCompareBuySimilar_spec.lua index ab283d5d205..25abafaa9f4 100644 --- a/spec/System/TestCompareBuySimilar_spec.lua +++ b/spec/System/TestCompareBuySimilar_spec.lua @@ -155,13 +155,10 @@ Implicits: 1 end local function getUniqueQuery(name, baseName) - local url = bs.buildURL({ title = name, baseName = baseName }, nil, { - realmDrop = { GetSelValue = function() return "PC" end }, - leagueDrop = { GetSelValue = function() return "Standard" end }, - listedDrop = { selIndex = 1 }, - }, {}, {}, true) - local queryJson = urlDecode(assert(url:match("[?&]q=(.+)$"))) - return dkjson.decode(queryJson) + local item = new("Item", "Rarity: UNIQUE\n" .. name .. "\n" .. baseName .. "\nImplicits: 0") + local query = getQuery(openPopup(item, "Jewel")) + main:ClosePopup() + return query end it("rebuilds the URL when league and listed status change", function() @@ -192,23 +189,6 @@ Implicits: 0]]) assert.equal("Ralakesh's Impatience", getQuery(controls).query.name) end) - it("preserves every loaded unique name in direct URLs", function() - local expectedNames = { - ["The Hateful Accuser "] = "The Hateful Accuser", - } - local uniqueCount = 0 - for _, typeList in pairs(data.uniques) do - for _, uniqueText in ipairs(typeList) do - local name, baseName = uniqueText:match("^([^\n]+)\n([^\n]+)") - assert.is_not_nil(name) - assert.is_not_nil(baseName) - assert.equal(expectedNames[name] or name, getUniqueQuery(name, baseName).query.name) - uniqueCount = uniqueCount + 1 - end - end - assert.is_true(uniqueCount > 0) - end) - it("removes a trailing unique ID without removing name punctuation", function() assert.equal("Uul-Netol's Embrace", getUniqueQuery("Uul-Netol's Embrace 1234", "Vaal Axe").query.name) end) diff --git a/src/Classes/CompareBuySimilar.lua b/src/Classes/CompareBuySimilar.lua index f0f7a8cb67a..b1c48e585d1 100644 --- a/src/Classes/CompareBuySimilar.lua +++ b/src/Classes/CompareBuySimilar.lua @@ -41,7 +41,7 @@ end -- Build the trade search URL based on popup selections -function M.buildURL(item, slotName, controls, modEntries, defenceEntries, isUnique) +local function buildURL(item, slotName, controls, modEntries, defenceEntries, isUnique) -- Determine realm and league from the popup's dropdowns local realmDisplayValue = controls.realmDrop and controls.realmDrop:GetSelValue() or "PC" local realm = REALM_API_IDS[realmDisplayValue] or "pc" @@ -339,7 +339,7 @@ function M.openPopup(item, slotName, primaryBuild) end local function rebuildUrl() - local result = M.buildURL(item, slotName, controls, modEntries, defenceEntries, isUnique) + local result = buildURL(item, slotName, controls, modEntries, defenceEntries, isUnique) uri = result end -- Helper to fetch and populate leagues for a given realm API id