Skip to content

Commit 425f8a3

Browse files
FenikSRT4LocalIdentity
andauthored
Add Loadout management UI (#1865)
* [ISSUE-9451] Loadout Management * Transferred ConfigTab and ConfigSetListControl changes from PoB1 * [ISSUE-9451] Loadout Management * Tranferred ItemSetListControl and ItemsTab changes from PoB1 * [ISSUE-9451] Loadout Management * Transferred SkillSetListControl and SkillsTab changes from PoB1 * [ISSUE-9451] Loadout Management * Transferred TreeTab and PassiveSpecListControl changes from PoB1 * [ISSUE-9451] Loadout Management * Transferred Build and BuildSetListControl changes from PoB1 * [ISSUE-9451] Loadout Management * Provide default "Default" title fallback when copying config sets with nil title * Provide default "Default" title fallback when copying item sets with nil title * Set newly created loadout as active immediately after creation in Build.lua * Fix items getting deleted from shared loadouts If an item belonged to 2 loadouts and 1 of them was deleted, it would wipe the items without checking to see if another loadout used them * Pressing F2 to rename the loadout now grabs the right name Renaming the loadout by pressing F2 would grab a table address instead of the name * Importing a shared item list fix When importing a shared item list it now uses CreateItemSet so it doesn't double add the set anymore * Avoid reapplying active loadout sets Only switch tree, item, skill, and config sets when the selected loadout uses a different set from the currently active one. This restores the previous loadout switching behaviour and prevents build initialisation from reprocessing stale tab state from the prior build, which caused the test suite to fail when resetting between skill tests. --------- Co-authored-by: LocalIdentity <localidentity2@gmail.com>
1 parent 99e9ed2 commit 425f8a3

19 files changed

Lines changed: 3407 additions & 315 deletions

spec/System/TestConfigTab_spec.lua

Lines changed: 385 additions & 17 deletions
Large diffs are not rendered by default.

spec/System/TestItemsTab_spec.lua

Lines changed: 411 additions & 0 deletions
Large diffs are not rendered by default.

spec/System/TestLoadouts_spec.lua

Lines changed: 1019 additions & 0 deletions
Large diffs are not rendered by default.

spec/System/TestSkillsTab_spec.lua

Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
describe("TestSkillsTab", function()
2+
before_each(function()
3+
newBuild()
4+
runCallback("OnFrame")
5+
end)
6+
7+
describe("SkillsTab", function()
8+
describe("NewSkillSet", function()
9+
it("Creates a new skill set with specified ID", function()
10+
local skillSetName = "New Skill Set"
11+
local skillSetId = 2
12+
build.skillsTab:NewSkillSet(skillSetId, skillSetName)
13+
14+
assert.are.equals(skillSetName, build.skillsTab.skillSets[skillSetId].title)
15+
assert.are.equals(skillSetId, build.skillsTab.skillSets[skillSetId].id)
16+
end)
17+
18+
it("Assigns auto-incremented ID when not specified", function()
19+
local skillSetName = "Auto ID Skill Set"
20+
local skillSet1 = build.skillsTab:NewSkillSet()
21+
local skillSet2 = build.skillsTab:NewSkillSet(nil, skillSetName)
22+
23+
assert.are.equals(2, skillSet1.id)
24+
assert.are.equals(3, skillSet2.id)
25+
assert.are.equals(skillSetName, build.skillsTab.skillSets[skillSet2.id].title)
26+
end)
27+
28+
it("Adds set to order list", function()
29+
local newTitle = "New Skill"
30+
31+
build.skillsTab:NewSkillSet(nil, newTitle)
32+
33+
assert.are.equals(2, #build.skillsTab.skillSetOrderList)
34+
assert.are.equals(2, build.skillsTab.skillSetOrderList[2])
35+
end)
36+
37+
it("Updates the mod flag", function()
38+
local newTitle = "New Skill"
39+
build.skillsTab.modFlag = false
40+
41+
build.skillsTab:NewSkillSet(nil, newTitle)
42+
43+
assert.is_true(build.skillsTab.modFlag)
44+
end)
45+
end)
46+
47+
describe("CopySkillSet", function()
48+
it("Copies a skill set with a new name", function()
49+
local newTitle = "Copied Skill"
50+
local newSkillSet = build.skillsTab:CopySkillSet(1, newTitle)
51+
52+
assert.is_not.same(build.skillsTab.skillSets[1], newSkillSet)
53+
assert.are.equals(newTitle, newSkillSet.title)
54+
assert.are.same(newSkillSet.socketGroupList, build.skillsTab.skillSets[1].socketGroupList)
55+
end)
56+
57+
it("Deep copies all socket groups and gems", function()
58+
local newTitle = "Copied Skill"
59+
60+
local testGem = { nameSpec = "TestGem", level = 20 }
61+
build.skillsTab.skillSets[1].socketGroupList = { { gemList = { testGem } } }
62+
63+
local newSkillSet = build.skillsTab:CopySkillSet(1, newTitle)
64+
65+
assert.are.same("TestGem", build.skillsTab.skillSets[1].socketGroupList[1].gemList[1].nameSpec)
66+
assert.are.same(20, build.skillsTab.skillSets[1].socketGroupList[1].gemList[1].level)
67+
build.skillsTab.skillSets[1].socketGroupList[1].gemList[1].nameSpec = "Modified"
68+
69+
assert.are.equals("TestGem", newSkillSet.socketGroupList[1].gemList[1].nameSpec)
70+
assert.are.equals(20, newSkillSet.socketGroupList[1].gemList[1].level)
71+
end)
72+
73+
it("Returns copied skill set with next ID", function()
74+
local newTitle = "Copied Skill"
75+
76+
local newSkillSet = build.skillsTab:CopySkillSet(1, newTitle)
77+
78+
assert.are.equals(2, newSkillSet.id)
79+
end)
80+
81+
it("Adds copied skill set to order list", function()
82+
local newTitle = "Copied Skill"
83+
84+
build.skillsTab:CopySkillSet(1, newTitle)
85+
86+
assert.are.equals(2, #build.skillsTab.skillSetOrderList)
87+
assert.are.equals(2, build.skillsTab.skillSetOrderList[2])
88+
end)
89+
90+
it("Updates the mod flag", function()
91+
local newTitle = "Copied Skill"
92+
build.skillsTab.modFlag = false
93+
94+
build.skillsTab:CopySkillSet(1, newTitle)
95+
96+
assert.is_true(build.skillsTab.modFlag)
97+
end)
98+
end)
99+
100+
describe("RenameSkillSet", function()
101+
it("Renames a skill set", function()
102+
local newTitle = "Renamed Skill"
103+
build.skillsTab:RenameSkillSet(1, newTitle)
104+
105+
assert.are.equals(newTitle, build.skillsTab.skillSets[1].title)
106+
end)
107+
108+
it("Does not rename non-existent skill set", function()
109+
build.skillsTab:RenameSkillSet(999, "Non-existent")
110+
111+
assert.is_nil(build.skillsTab.skillSets[999])
112+
end)
113+
114+
it("Updates the mod flag", function()
115+
local newTitle = "Renamed Skill"
116+
build.skillsTab.modFlag = false
117+
118+
build.skillsTab:RenameSkillSet(1, newTitle)
119+
120+
assert.is_true(build.skillsTab.modFlag)
121+
end)
122+
end)
123+
124+
describe("DeleteSkillSet", function()
125+
it("Deletes a skill set and its order entry", function()
126+
local skillSetName = "Skill To Delete"
127+
build.skillsTab:NewSkillSet(2, skillSetName)
128+
129+
build.skillsTab:DeleteSkillSet(2, 2)
130+
131+
assert.is_nil(build.skillsTab.skillSets[2])
132+
assert.are.equals(1, #build.skillsTab.skillSetOrderList)
133+
end)
134+
135+
it("allows deletion of the last skill set", function()
136+
local lastSkill = 1
137+
138+
assert.are.equals(1, #build.skillsTab.skillSetOrderList)
139+
build.skillsTab:DeleteSkillSet(lastSkill, 1)
140+
141+
assert.are.equals(0, #build.skillsTab.skillSetOrderList)
142+
assert.is_nil(build.skillsTab.skillSets[lastSkill])
143+
end)
144+
end)
145+
146+
describe("SetActiveSkillSet", function()
147+
it("Switches to a valid skill set", function()
148+
local skillSetName = "New Skill"
149+
build.skillsTab:NewSkillSet(2, skillSetName)
150+
151+
build.skillsTab:SetActiveSkillSet(2)
152+
153+
assert.are.equals(2, build.skillsTab.activeSkillSetId)
154+
assert.are.same(build.skillsTab.skillSets[2].socketGroupList, build.skillsTab.socketGroupList)
155+
end)
156+
157+
it("Defaults to first skill set if invalid ID provided", function()
158+
build.skillsTab:SetActiveSkillSet(999)
159+
160+
assert.are.equals(1, build.skillsTab.activeSkillSetId)
161+
end)
162+
end)
163+
end)
164+
165+
describe("SkillsSetService", function()
166+
local skillsSetService
167+
168+
before_each(function()
169+
skillsSetService = new("SkillsSetService", build.skillsTab)
170+
end)
171+
172+
describe("NewSkillSet", function()
173+
it("Creates a new skill set via service", function()
174+
local skillSetName = "Service New Skill"
175+
skillsSetService:NewSkillSet(skillSetName)
176+
177+
assert.are.equals(2, #build.skillsTab.skillSetOrderList)
178+
assert.are.equals(skillSetName, build.skillsTab.skillSets[2].title)
179+
end)
180+
181+
it("Sets newly created skill as active", function()
182+
skillsSetService:NewSkillSet("New From Service")
183+
184+
assert.are.equals(2, build.skillsTab.activeSkillSetId)
185+
end)
186+
187+
it("Adds an undo state", function()
188+
local newTitle = "New Skill"
189+
190+
local undoCountBefore = #build.skillsTab.undo
191+
skillsSetService:NewSkillSet(newTitle)
192+
193+
assert.are.equals(#build.skillsTab.undo, undoCountBefore + 1)
194+
end)
195+
end)
196+
197+
describe("CopySkillSet", function()
198+
it("Copies a skill set via service", function()
199+
local skillSetName = "Copied via Service"
200+
skillsSetService:CopySkillSet(1, skillSetName)
201+
local skillSetId = build.skillsTab.skillSetOrderList[2]
202+
203+
assert.are.equals(skillSetName, build.skillsTab.skillSets[skillSetId].title)
204+
end)
205+
206+
it("Sets copied skill set as active via service", function()
207+
skillsSetService:NewSkillSet("Skill")
208+
skillsSetService:CopySkillSet(1, "Service Copy")
209+
local skillSetId = build.skillsTab.skillSetOrderList[3]
210+
211+
assert.are.equals(skillSetId, build.skillsTab.activeSkillSetId)
212+
end)
213+
214+
it("Adds an undo state when copying a skill set", function()
215+
local undoCountBefore = #build.skillsTab.undo
216+
skillsSetService:CopySkillSet(1, "Copied Skill")
217+
218+
assert.are.equals(#build.skillsTab.undo, undoCountBefore + 1)
219+
end)
220+
end)
221+
222+
describe("RenameSkillSet", function()
223+
it("Renames a skill set via service", function()
224+
local skillSetName = "Original Name"
225+
skillsSetService:NewSkillSet(skillSetName)
226+
local skillSetId = build.skillsTab.skillSetOrderList[2]
227+
228+
skillsSetService:RenameSkillSet(skillSetId, "New Name")
229+
230+
assert.are.equals("New Name", build.skillsTab.skillSets[skillSetId].title)
231+
end)
232+
233+
it("Does not rename non-existent skill set", function()
234+
skillsSetService:RenameSkillSet(10, "Non-existent")
235+
236+
assert.is_nil(build.skillsTab.skillSets[10])
237+
end)
238+
239+
it("Adds an undo state when renaming", function()
240+
local skillSetName = "Skill To Rename"
241+
skillsSetService:NewSkillSet(skillSetName)
242+
local skillSetId = build.skillsTab.skillSetOrderList[2]
243+
244+
local undoCountBefore = #build.skillsTab.undo
245+
skillsSetService:RenameSkillSet(skillSetId, "Renamed Skill")
246+
247+
assert.are.equals(#build.skillsTab.undo, undoCountBefore + 1)
248+
end)
249+
end)
250+
251+
describe("DeleteSkillSet", function()
252+
it("Deletes a skill set via service", function()
253+
skillsSetService:NewSkillSet("Service Delete Me")
254+
255+
skillsSetService:DeleteSkillSet(2, 2)
256+
257+
assert.is_nil(build.skillsTab.skillSets[2])
258+
assert.is_nil(build.skillsTab.skillSetOrderList[2])
259+
end)
260+
261+
it("Switches active skill set when deleting current", function()
262+
skillsSetService:NewSkillSet("Service Delete Me")
263+
local skillToKeep = 1
264+
local skillToDelete = 2
265+
266+
assert.are.equals(skillToDelete, build.skillsTab.activeSkillSetId)
267+
268+
skillsSetService:DeleteSkillSet(skillToDelete, 2)
269+
270+
assert.are.equals(skillToKeep, build.skillsTab.activeSkillSetId)
271+
end)
272+
273+
it("Does not delete last skill set via service", function()
274+
skillsSetService:NewSkillSet("Last One")
275+
276+
skillsSetService:DeleteSkillSet(1, 1)
277+
278+
assert.are.equals(1, #build.skillsTab.skillSetOrderList)
279+
end)
280+
281+
it("Adds an undo state when deleting", function()
282+
local undoCountBefore = #build.skillsTab.undo
283+
local undoCountAfterNew = #build.skillsTab.undo
284+
skillsSetService:NewSkillSet("Undo Delete Test")
285+
286+
skillsSetService:DeleteSkillSet(2, 2)
287+
288+
assert.are.equals(#build.skillsTab.undo, undoCountBefore + 2)
289+
end)
290+
end)
291+
292+
293+
294+
describe("Integration", function()
295+
it("Completes a skill set lifecycle", function()
296+
local skill1Name = "First Skill"
297+
local skill2Name = "Second Skill"
298+
299+
skillsSetService:NewSkillSet(skill1Name)
300+
assert.are.equals("First Skill", build.skillsTab.skillSets[2].title)
301+
302+
skillsSetService:CopySkillSet(2, skill2Name)
303+
assert.are.equals("Second Skill", build.skillsTab.skillSets[3].title)
304+
end)
305+
end)
306+
end)
307+
308+
describe("SkillSetStateManagement", function()
309+
local skillsSetService
310+
311+
before_each(function()
312+
skillsSetService = new("SkillsSetService", build.skillsTab)
313+
end)
314+
315+
describe("Socket group persistence", function()
316+
it("Preserves socket groups across skill set switches", function()
317+
skillsSetService:NewSkillSet("Set 1")
318+
skillsSetService:NewSkillSet("Set 2")
319+
320+
local testSocketGroup = {
321+
label = "Test Group",
322+
enabled = true,
323+
gemList = {
324+
{ nameSpec = "TestGem", level = 20, quality = 0, qualityId = "Default", enabled = true, count = 1 }
325+
}
326+
}
327+
328+
build.skillsTab.skillSets[1].socketGroupList[1] = testSocketGroup
329+
build.skillsTab:SetActiveSkillSet(1)
330+
331+
assert.are.same(testSocketGroup, build.skillsTab.skillSets[1].socketGroupList[1])
332+
333+
build.skillsTab:SetActiveSkillSet(3)
334+
build.skillsTab.skillSets[3].socketGroupList[1] = { label = "Different" }
335+
336+
build.skillsTab:SetActiveSkillSet(1)
337+
assert.are.same(testSocketGroup, build.skillsTab.skillSets[1].socketGroupList[1])
338+
end)
339+
340+
it("Preserves nested gem data across skill set switches", function()
341+
skillsSetService:NewSkillSet("Set 1")
342+
skillsSetService:NewSkillSet("Set 2")
343+
skillsSetService:NewSkillSet("Set 3")
344+
345+
local testGem = { nameSpec = "TestGem", level = 20, quality = 20 }
346+
build.skillsTab.skillSets[3].socketGroupList = { { gemList = { testGem } } }
347+
348+
build.skillsTab:SetActiveSkillSet(3)
349+
assert.are.same(testGem, build.skillsTab.skillSets[3].socketGroupList[1].gemList[1])
350+
end)
351+
end)
352+
353+
describe("Default values", function()
354+
it("Initializes new skill sets with default structure", function()
355+
assert.is_not_nil(build.skillsTab.skillSets[1])
356+
assert.is_not_nil(build.skillsTab.skillSets[1].socketGroupList)
357+
assert.is_not_nil(build.skillsTab.skillSets[1].id)
358+
end)
359+
360+
it("Copies socket group structure when copying skill sets", function()
361+
local testSocketGroup = {
362+
label = "Original",
363+
enabled = false,
364+
gemList = {}
365+
}
366+
build.skillsTab.skillSets[1].socketGroupList[1] = testSocketGroup
367+
368+
local newSkillSet = build.skillsTab:CopySkillSet(1, "Copy Test")
369+
370+
assert.are.equals("Original", newSkillSet.socketGroupList[1].label)
371+
assert.is_false(newSkillSet.socketGroupList[1].enabled)
372+
end)
373+
end)
374+
end)
375+
end)

0 commit comments

Comments
 (0)