Repository: gridhead/gi-loadouts (language: python)

Issue:
Establish testing of the functional part of the codebase with satisfactory coverage
Establish testing of the functional part of the codebase with satisfactory coverage
Add test of type of weapon
Add test of type of weapon and add `docstrings` on `test/type`


Discussion / hints:

Fix patch:
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 00000000..6e02b66f
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,10 @@
+[run]
+omit =
+    gi_loadouts/face/rsrc/arti/*.py
+    gi_loadouts/face/rsrc/char/*.py
+    gi_loadouts/face/rsrc/data/*.py
+    gi_loadouts/face/rsrc/font/*.py
+    gi_loadouts/face/rsrc/pmon/*.py
+    gi_loadouts/face/rsrc/rare/*.py
+    gi_loadouts/face/rsrc/vson/*.py
+    gi_loadouts/face/rsrc/weap/*.py
diff --git a/.github/workflows/wipe.yml b/.github/workflows/wipe.yml
index bc1fe5aa..821d1d5d 100644
--- a/.github/workflows/wipe.yml
+++ b/.github/workflows/wipe.yml
@@ -1,5 +1,5 @@
 ---
-name: Manage artifacts to retain four recent artifacts
+name: Managing artifacts to retain four recent artifacts
 on: [push]
 jobs:
   ci-wipe:
diff --git a/assets/wind.ui b/assets/wind.ui
index b2a66ef0..6116cbed 100644
--- a/assets/wind.ui
+++ b/assets/wind.ui
@@ -3004,7 +3004,7 @@
        </size>
       </property>
       <property name="toolTip">
-       <string>Load</string>
+       <string>Scan</string>
       </property>
       <property name="styleSheet">
        <string notr="true">#arti_ccol_scan {font: 87 10pt &quot;Font Awesome 6 Free Solid&quot;;}</string>
@@ -4055,7 +4055,7 @@
        </size>
       </property>
       <property name="toolTip">
-       <string>Load</string>
+       <string>Scan</string>
       </property>
       <property name="styleSheet">
        <string notr="true">#arti_gboe_scan {font: 87 10pt &quot;Font Awesome 6 Free Solid&quot;;}</string>
@@ -4323,7 +4323,7 @@
        </size>
       </property>
       <property name="toolTip">
-       <string>Load</string>
+       <string>Scan</string>
       </property>
       <property name="styleSheet">
        <string notr="true">#arti_sdoe_scan {font: 87 10pt &quot;Font Awesome 6 Free Solid&quot;;}</string>
@@ -4591,7 +4591,7 @@
        </size>
       </property>
       <property name="toolTip">
-       <string>Load</string>
+       <string>Scan</string>
       </property>
       <property name="styleSheet">
        <string notr="true">#arti_pmod_scan {font: 87 10pt &quot;Font Awesome 6 Free Solid&quot;;}</string>
@@ -4859,7 +4859,7 @@
        </size>
       </property>
       <property name="toolTip">
-       <string>Load</string>
+       <string>Scan</string>
       </property>
       <property name="styleSheet">
        <string notr="true">#arti_fwol_scan {font: 87 10pt &quot;Font Awesome 6 Free Solid&quot;;}</string>
diff --git a/gi_loadouts/conf.py b/gi_loadouts/conf.py
index 0411a968..60a9f3ef 100644
--- a/gi_loadouts/conf.py
+++ b/gi_loadouts/conf.py
@@ -1,10 +1,17 @@
 from platform import system
 
-if system() == "Windows":
-    tessexec = "C:\\Program Files\\Tesseract-OCR\\tesseract.exe"
-else:
-    tessexec = "/usr/bin/tesseract"
+
+def get_tessexec_path():
+    if system() == "Windows":
+        return "C:\\Program Files\\Tesseract-OCR\\tesseract.exe"
+    else:
+        return "/usr/bin/tesseract"
+
+tessexec = get_tessexec_path()
 
 tempname = ""
 temppath = ""
 stattime = 5000
+
+data_prefix = "gi-loadouts-"
+data_suffix = ".traineddata"
diff --git a/gi_loadouts/data/arti/__init__.py b/gi_loadouts/data/arti/__init__.py
index 199bde15..60b7e19c 100644
--- a/gi_loadouts/data/arti/__init__.py
+++ b/gi_loadouts/data/arti/__init__.py
@@ -317,7 +317,7 @@
         fwol=rosj.fwol(), pmod=rosj.pmod(), sdoe=rosj.sdoe(), gboe=rosj.gboe(), ccol=rosj.ccol(),
         pairdata=rosj.team.__pairdata__, pairtext=rosj.team.__pairtext__,
         quaddata=rosj.team.__quaddata__, quadtext=rosj.team.__quadtext__,
-        rare=[Rare.Star_4, Rare.Star_5],
+        rare=[Rare.Star_3, Rare.Star_4],
         file=rosj.__name__.split(".")[-1]
     ),
     rcbl.team.__teamname__: ArtifactTeam(
diff --git a/gi_loadouts/data/char/__init__.py b/gi_loadouts/data/char/__init__.py
index 0912e29e..e5df46e7 100644
--- a/gi_loadouts/data/char/__init__.py
+++ b/gi_loadouts/data/char/__init__.py
@@ -88,92 +88,92 @@
 from gi_loadouts.data.char.zhongli import Zhongli
 
 __charmaps__ = {
-    "Aether": Aether(),
-    "Albedo": Albedo(),
-    "Alhaitham": Alhaitham(),
-    "Aloy": Aloy(),
-    "Amber": Amber(),
-    "Arataki Itto": AratakiItto(),
-    "Arlecchino": Arlecchino(),
-    "Baizhu": Baizhu(),
-    "Barbara": Barbara(),
-    "Beidou": Beidou(),
-    "Bennett": Bennett(),
-    "Candace": Candace(),
-    "Charlotte": Charlotte(),
-    "Chevreuse": Chevreuse(),
-    "Chiori": Chiori(),
-    "Chongyun": Chongyun(),
-    "Clorinde": Clorinde(),
-    "Collei": Collei(),
-    "Cyno": Cyno(),
-    "Dehya": Dehya(),
-    "Diluc": Diluc(),
-    "Diona": Diona(),
-    "Dori": Dori(),
-    "Emilie": Emilie(),
-    "Eula": Eula(),
-    "Faruzan": Faruzan(),
-    "Fischl": Fischl(),
-    "Freminet": Freminet(),
-    "Furina": Furina(),
-    "Gaming": Gaming(),
-    "Ganyu": Ganyu(),
-    "Gorou": Gorou(),
-    "Hu Tao": HuTao(),
-    "Jean": Jean(),
-    "Kachina": Kachina(),
-    "Kaedehara Kazuha": KaedeharaKazuha(),
-    "Kamisato Ayaka": KamisatoAyaka(),
-    "Kamisato Ayato": KamisatoAyato(),
-    "Kaeya": Kaeya(),
-    "Kaveh": Kaveh(),
-    "Keqing": Keqing(),
-    "Kirara": Kirara(),
-    "Klee": Klee(),
-    "Kujou Sara": KujouSara(),
-    "Kuki Shinobu": KukiShinobu(),
-    "Layla": Layla(),
-    "Lisa": Lisa(),
-    "Lumine": Lumine(),
-    "Lynette": Lynette(),
-    "Lyney": Lyney(),
-    "Mika": Mika(),
-    "Mona": Mona(),
-    "Mualani": Mualani(),
-    "Nahida": Nahida(),
-    "Navia": Navia(),
-    "Neuvillette": Neuvillette(),
-    "Nilou": Nilou(),
-    "Ningguang": Ningguang(),
-    "Noelle": Noelle(),
-    "Qiqi": Qiqi(),
-    "Raiden Shogun": RaidenShogun(),
-    "Razor": Razor(),
-    "Rosaria": Rosaria(),
-    "Sangonomiya Kokomi": SangonomiyaKokomi(),
-    "Sayu": Sayu(),
-    "Sethos": Sethos(),
-    "Shenhe": Shenhe(),
-    "Shikanoin Heizou": ShikanoinHeizou(),
-    "Sigewinne": Sigewinne(),
-    "Sucrose": Sucrose(),
-    "Tartaglia": Tartaglia(),
-    "Thoma": Thoma(),
-    "Tighnari": Tighnari(),
-    "Venti": Venti(),
-    "Wanderer": Wanderer(),
-    "Wriothesley": Wriothesley(),
-    "Xiangling": Xiangling(),
-    "Xianyun": Xianyun(),
-    "Xiao": Xiao(),
-    "Xingqiu": Xingqiu(),
-    "Xinyan": Xinyan(),
-    "Yae Miko": YaeMiko(),
-    "Yanfei": Yanfei(),
-    "Yaoyao": Yaoyao(),
-    "Yelan": Yelan(),
-    "Yoimiya": Yoimiya(),
-    "Yun Jin": YunJin(),
-    "Zhongli": Zhongli(),
+    "Aether": Aether,
+    "Albedo": Albedo,
+    "Alhaitham": Alhaitham,
+    "Aloy": Aloy,
+    "Amber": Amber,
+    "Arataki Itto": AratakiItto,
+    "Arlecchino": Arlecchino,
+    "Baizhu": Baizhu,
+    "Barbara": Barbara,
+    "Beidou": Beidou,
+    "Bennett": Bennett,
+    "Candace": Candace,
+    "Charlotte": Charlotte,
+    "Chevreuse": Chevreuse,
+    "Chiori": Chiori,
+    "Chongyun": Chongyun,
+    "Clorinde": Clorinde,
+    "Collei": Collei,
+    "Cyno": Cyno,
+    "Dehya": Dehya,
+    "Diluc": Diluc,
+    "Diona": Diona,
+    "Dori": Dori,
+    "Emilie": Emilie,
+    "Eula": Eula,
+    "Faruzan": Faruzan,
+    "Fischl": Fischl,
+    "Freminet": Freminet,
+    "Furina": Furina,
+    "Gaming": Gaming,
+    "Ganyu": Ganyu,
+    "Gorou": Gorou,
+    "Hu Tao": HuTao,
+    "Jean": Jean,
+    "Kachina": Kachina,
+    "Kaedehara Kazuha": KaedeharaKazuha,
+    "Kamisato Ayaka": KamisatoAyaka,
+    "Kamisato Ayato": KamisatoAyato,
+    "Kaeya": Kaeya,
+    "Kaveh": Kaveh,
+    "Keqing": Keqing,
+    "Kirara": Kirara,
+    "Klee": Klee,
+    "Kujou Sara": KujouSara,
+    "Kuki Shinobu": KukiShinobu,
+    "Layla": Layla,
+    "Lisa": Lisa,
+    "Lumine": Lumine,
+    "Lynette": Lynette,
+    "Lyney": Lyney,
+    "Mika": Mika,
+    "Mona": Mona,
+    "Mualani": Mualani,
+    "Nahida": Nahida,
+    "Navia": Navia,
+    "Neuvillette": Neuvillette,
+    "Nilou": Nilou,
+    "Ningguang": Ningguang,
+    "Noelle": Noelle,
+    "Qiqi": Qiqi,
+    "Raiden Shogun": RaidenShogun,
+    "Razor": Razor,
+    "Rosaria": Rosaria,
+    "Sangonomiya Kokomi": SangonomiyaKokomi,
+    "Sayu": Sayu,
+    "Sethos": Sethos,
+    "Shenhe": Shenhe,
+    "Shikanoin Heizou": ShikanoinHeizou,
+    "Sigewinne": Sigewinne,
+    "Sucrose": Sucrose,
+    "Tartaglia": Tartaglia,
+    "Thoma": Thoma,
+    "Tighnari": Tighnari,
+    "Venti": Venti,
+    "Wanderer": Wanderer,
+    "Wriothesley": Wriothesley,
+    "Xiangling": Xiangling,
+    "Xianyun": Xianyun,
+    "Xiao": Xiao,
+    "Xingqiu": Xingqiu,
+    "Xinyan": Xinyan,
+    "Yae Miko": YaeMiko,
+    "Yanfei": Yanfei,
+    "Yaoyao": Yaoyao,
+    "Yelan": Yelan,
+    "Yoimiya": Yoimiya,
+    "Yun Jin": YunJin,
+    "Zhongli": Zhongli,
 }
diff --git a/gi_loadouts/data/char/diona.py b/gi_loadouts/data/char/diona.py
index ac764aca..5fb57260 100644
--- a/gi_loadouts/data/char/diona.py
+++ b/gi_loadouts/data/char/diona.py
@@ -6,7 +6,7 @@
 
 
 class Diona(Char):
-    __statdata__: dict = {0: 0.0, 1: 0.0, 2: 4.8, 3: 9.6, 4: 9.6, 5: 14.4, 6: 19.2}
+    __statdata__: dict = {0: 0.0, 1: 0.0, 2: 6.0, 3: 12.0, 4: 12.0, 5: 18.0, 6: 24.0}
     __statname__: STAT = STAT.damage_bonus_cryo_perc
     name: str = "Diona"
     rare: Rare = Rare.Star_4
diff --git a/gi_loadouts/data/char/sigewinne.py b/gi_loadouts/data/char/sigewinne.py
index 5e0bdda4..07c74edc 100644
--- a/gi_loadouts/data/char/sigewinne.py
+++ b/gi_loadouts/data/char/sigewinne.py
@@ -7,7 +7,7 @@
 
 class Sigewinne(Char):
     __statdata__: dict = {0: 0.0, 1: 0.0, 2: 7.2, 3: 14.4, 4: 14.4, 5: 21.6, 6: 28.8}
-    __statname__: STAT = STAT.healing_bonus_perc
+    __statname__: STAT = STAT.health_points_perc
     name: str = "Sigewinne"
     rare: Rare = Rare.Star_5
     base: BaseStat = BaseStat(attack=14.9891, defense=38.8895, health_points=1039.1188)
diff --git a/gi_loadouts/data/weap/__init__.py b/gi_loadouts/data/weap/__init__.py
index f7f1c0b7..a0071bfd 100644
--- a/gi_loadouts/data/weap/__init__.py
+++ b/gi_loadouts/data/weap/__init__.py
@@ -3,7 +3,7 @@
 Family = {
     "Bow": bows.BowsDict,
     "Catalyst": catalysts.CatalystsDict,
-    "Claymore": claymores.ClaymoreDict,
+    "Claymore": claymores.ClaymoresDict,
     "Polearm": polearms.PolearmsDict,
     "Sword": swords.SwordsDict,
 }
diff --git a/gi_loadouts/data/weap/bows/__init__.py b/gi_loadouts/data/weap/bows/__init__.py
index cda431b6..4254f77f 100644
--- a/gi_loadouts/data/weap/bows/__init__.py
+++ b/gi_loadouts/data/weap/bows/__init__.py
@@ -42,44 +42,44 @@
 )
 
 BowsDict = {
-    ayht.AlleyHunter().name: ayht.AlleyHunter(),
-    asbw.AmosBow().name: asbw.AmosBow(),
-    aqsl.AquaSimulacra().name: aqsl.AquaSimulacra(),
-    bcwb.BlackcliffWarbow().name: bcwb.BlackcliffWarbow(),
-    cnbr.ChainBreaker().name: cnbr.ChainBreaker(),
-    cdfg.Cloudforged().name: cdfg.Cloudforged(),
-    cpbw.CompoundBow().name: cpbw.CompoundBow(),
-    efte.ElegyfortheEnd().name: efte.ElegyfortheEnd(),
-    eotl.EndoftheLine().name: eotl.EndoftheLine(),
-    fdtl.FadingTwilight().name: fdtl.FadingTwilight(),
-    fvwb.FavoniusWarbow().name: fvwb.FavoniusWarbow(),
-    hmym.Hamayumi().name: hmym.Hamayumi(),
-    htbw.HuntersBow().name: htbw.HuntersBow(),
-    htpt.HuntersPath().name: htpt.HuntersPath(),
-    ispc.IbisPiercer().name: ispc.IbisPiercer(),
-    kgsr.KingsSquire().name: kgsr.KingsSquire(),
-    msgr.Messenger().name: msgr.Messenger(),
-    mnwz.MitternachtsWaltz().name: mnwz.MitternachtsWaltz(),
-    momo.MouunsMoon().name: momo.MouunsMoon(),
-    prsr.PolarStar().name: prsr.PolarStar(),
-    pdtr.Predator().name: pdtr.Predator(),
-    ptct.PrototypeCrescent().name: ptct.PrototypeCrescent(),
-    rnge.RangeGauge().name: rnge.RangeGauge(),
-    rvbw.RavenBow().name: rvbw.RavenBow(),
-    rcbw.RecurveBow().name: rcbw.RecurveBow(),
-    rybw.RoyalBow().name: rybw.RoyalBow(),
-    rust.Rust().name: rust.Rust(),
-    sfbw.SacrificialBow().name: sfbw.SacrificialBow(),
-    sobs.ScionoftheBlazingSun().name: sobs.ScionoftheBlazingSun(),
-    shbw.SeasonedHuntersBow().name: shbw.SeasonedHuntersBow(),
-    ssoh.SharpshootersOath().name: ssoh.SharpshootersOath(),
-    sshs.SilvershowerHeartstrings().name: sshs.SilvershowerHeartstrings(),
-    swhp.SkywardHarp().name: swhp.SkywardHarp(),
-    sgst.Slingshot().name: sgst.Slingshot(),
-    sosn.SongofStillness().name: sosn.SongofStillness(),
-    tfgm.TheFirstGreatMagic().name: tfgm.TheFirstGreatMagic(),
-    tsgs.TheStringless().name: tsgs.TheStringless(),
-    tvsh.TheViridescentHunt().name: tvsh.TheViridescentHunt(),
-    tdpe.ThunderingPulse().name: tdpe.ThunderingPulse(),
-    wboe.WindblumeOde().name: wboe.WindblumeOde()
+    "Alley Hunter": ayht.AlleyHunter,
+    "Amos' Bow": asbw.AmosBow,
+    "Aqua Simulacra": aqsl.AquaSimulacra,
+    "Blackcliff Warbow": bcwb.BlackcliffWarbow,
+    "Chain Breaker": cnbr.ChainBreaker,
+    "Cloudforged": cdfg.Cloudforged,
+    "Compound Bow": cpbw.CompoundBow,
+    "Elegy for the End": efte.ElegyfortheEnd,
+    "End of the Line": eotl.EndoftheLine,
+    "Fading Twilight": fdtl.FadingTwilight,
+    "Favonius Warbow": fvwb.FavoniusWarbow,
+    "Hamayumi": hmym.Hamayumi,
+    "Hunter's Bow": htbw.HuntersBow,
+    "Hunter's Path": htpt.HuntersPath,
+    "Ibis Piercer": ispc.IbisPiercer,
+    "King's Squire": kgsr.KingsSquire,
+    "Messenger": msgr.Messenger,
+    "Mitternachts Waltz": mnwz.MitternachtsWaltz,
+    "Mouun's Moon": momo.MouunsMoon,
+    "Polar Star": prsr.PolarStar,
+    "Predator": pdtr.Predator,
+    "Prototype Crescent": ptct.PrototypeCrescent,
+    "Range Gauge": rnge.RangeGauge,
+    "Raven Bow": rvbw.RavenBow,
+    "Recurve Bow": rcbw.RecurveBow,
+    "Royal Bow": rybw.RoyalBow,
+    "Rust": rust.Rust,
+    "Sacrificial Bow": sfbw.SacrificialBow,
+    "Scion of the Blazing Sun": sobs.ScionoftheBlazingSun,
+    "Seasoned Hunter's Bow": shbw.SeasonedHuntersBow,
+    "Sharpshooter's Oath": ssoh.SharpshootersOath,
+    "Silvershower Heartstrings": sshs.SilvershowerHeartstrings,
+    "Skyward Harp": swhp.SkywardHarp,
+    "Slingshot": sgst.Slingshot,
+    "Song of Stillness": sosn.SongofStillness,
+    "The First Great Magic": tfgm.TheFirstGreatMagic,
+    "The Stringless": tsgs.TheStringless,
+    "The Viridescent Hunt": tvsh.TheViridescentHunt,
+    "Thundering Pulse": tdpe.ThunderingPulse,
+    "Windblume Ode": wboe.WindblumeOde
 }
diff --git a/gi_loadouts/data/weap/bows/aqsl.py b/gi_loadouts/data/weap/bows/aqsl.py
index eb6606a3..a059ec96 100644
--- a/gi_loadouts/data/weap/bows/aqsl.py
+++ b/gi_loadouts/data/weap/bows/aqsl.py
@@ -2,13 +2,13 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class AquaSimulacra(Bow):
     name: str = "Aqua Simulacra"
     seco_stat: WeaponStat = WeaponStat(stat_name=WeaponStatType.critical_damage_perc, stat_data=19.2)
-    tier: Tier = Tier.Tier_2
+    tier: Tier = Tier.Tier_1
     rare: Rare = Rare.Star_5
     refi_name: str = "The Cleansing Form"
     refi_list: List[str] = [
diff --git a/gi_loadouts/data/weap/bows/asbw.py b/gi_loadouts/data/weap/bows/asbw.py
index 305d1379..5fb81201 100644
--- a/gi_loadouts/data/weap/bows/asbw.py
+++ b/gi_loadouts/data/weap/bows/asbw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class AmosBow(Bow):
diff --git a/gi_loadouts/data/weap/bows/ayht.py b/gi_loadouts/data/weap/bows/ayht.py
index 6f7c583c..442f2c6c 100644
--- a/gi_loadouts/data/weap/bows/ayht.py
+++ b/gi_loadouts/data/weap/bows/ayht.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class AlleyHunter(Bow):
diff --git a/gi_loadouts/data/weap/bows/bcwb.py b/gi_loadouts/data/weap/bows/bcwb.py
index 27025d1a..33868f8a 100644
--- a/gi_loadouts/data/weap/bows/bcwb.py
+++ b/gi_loadouts/data/weap/bows/bcwb.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BlackcliffWarbow(Bow):
diff --git a/gi_loadouts/data/weap/bows/cdfg.py b/gi_loadouts/data/weap/bows/cdfg.py
index c7f4cc92..49a7a92e 100644
--- a/gi_loadouts/data/weap/bows/cdfg.py
+++ b/gi_loadouts/data/weap/bows/cdfg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Cloudforged(Bow):
diff --git a/gi_loadouts/data/weap/bows/cnbr.py b/gi_loadouts/data/weap/bows/cnbr.py
index 5ee11339..d0982ad7 100644
--- a/gi_loadouts/data/weap/bows/cnbr.py
+++ b/gi_loadouts/data/weap/bows/cnbr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ChainBreaker(Bow):
diff --git a/gi_loadouts/data/weap/bows/cpbw.py b/gi_loadouts/data/weap/bows/cpbw.py
index 6b3820d3..97d89d31 100644
--- a/gi_loadouts/data/weap/bows/cpbw.py
+++ b/gi_loadouts/data/weap/bows/cpbw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class CompoundBow(Bow):
diff --git a/gi_loadouts/data/weap/bows/efte.py b/gi_loadouts/data/weap/bows/efte.py
index d46127f3..ceb560fa 100644
--- a/gi_loadouts/data/weap/bows/efte.py
+++ b/gi_loadouts/data/weap/bows/efte.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ElegyfortheEnd(Bow):
diff --git a/gi_loadouts/data/weap/bows/eotl.py b/gi_loadouts/data/weap/bows/eotl.py
index ada35064..18e50c0c 100644
--- a/gi_loadouts/data/weap/bows/eotl.py
+++ b/gi_loadouts/data/weap/bows/eotl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class EndoftheLine(Bow):
diff --git a/gi_loadouts/data/weap/bows/fdtl.py b/gi_loadouts/data/weap/bows/fdtl.py
index 74db9cd5..0dadc0ca 100644
--- a/gi_loadouts/data/weap/bows/fdtl.py
+++ b/gi_loadouts/data/weap/bows/fdtl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FadingTwilight(Bow):
diff --git a/gi_loadouts/data/weap/bows/fvwb.py b/gi_loadouts/data/weap/bows/fvwb.py
index 26bc3a5e..a3e0b59d 100644
--- a/gi_loadouts/data/weap/bows/fvwb.py
+++ b/gi_loadouts/data/weap/bows/fvwb.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FavoniusWarbow(Bow):
diff --git a/gi_loadouts/data/weap/bows/hmym.py b/gi_loadouts/data/weap/bows/hmym.py
index 26be7f32..fbba1356 100644
--- a/gi_loadouts/data/weap/bows/hmym.py
+++ b/gi_loadouts/data/weap/bows/hmym.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Hamayumi(Bow):
diff --git a/gi_loadouts/data/weap/bows/htbw.py b/gi_loadouts/data/weap/bows/htbw.py
index 882a840e..711e2c7a 100644
--- a/gi_loadouts/data/weap/bows/htbw.py
+++ b/gi_loadouts/data/weap/bows/htbw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class HuntersBow(Bow):
diff --git a/gi_loadouts/data/weap/bows/htpt.py b/gi_loadouts/data/weap/bows/htpt.py
index ff74ad7f..8a1b03e7 100644
--- a/gi_loadouts/data/weap/bows/htpt.py
+++ b/gi_loadouts/data/weap/bows/htpt.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class HuntersPath(Bow):
diff --git a/gi_loadouts/data/weap/bows/ispc.py b/gi_loadouts/data/weap/bows/ispc.py
index 173fa912..595f4bce 100644
--- a/gi_loadouts/data/weap/bows/ispc.py
+++ b/gi_loadouts/data/weap/bows/ispc.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class IbisPiercer(Bow):
diff --git a/gi_loadouts/data/weap/bows/kgsr.py b/gi_loadouts/data/weap/bows/kgsr.py
index 14d26341..af2453dc 100644
--- a/gi_loadouts/data/weap/bows/kgsr.py
+++ b/gi_loadouts/data/weap/bows/kgsr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class KingsSquire(Bow):
diff --git a/gi_loadouts/data/weap/bows/mnwz.py b/gi_loadouts/data/weap/bows/mnwz.py
index 1955ac5d..d44bd1f6 100644
--- a/gi_loadouts/data/weap/bows/mnwz.py
+++ b/gi_loadouts/data/weap/bows/mnwz.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MitternachtsWaltz(Bow):
diff --git a/gi_loadouts/data/weap/bows/momo.py b/gi_loadouts/data/weap/bows/momo.py
index 395dd48f..0e3c00ed 100644
--- a/gi_loadouts/data/weap/bows/momo.py
+++ b/gi_loadouts/data/weap/bows/momo.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MouunsMoon(Bow):
diff --git a/gi_loadouts/data/weap/bows/msgr.py b/gi_loadouts/data/weap/bows/msgr.py
index 57a6eb92..d21c0ac7 100644
--- a/gi_loadouts/data/weap/bows/msgr.py
+++ b/gi_loadouts/data/weap/bows/msgr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Messenger(Bow):
diff --git a/gi_loadouts/data/weap/bows/pdtr.py b/gi_loadouts/data/weap/bows/pdtr.py
index e1d23d09..0c8fd543 100644
--- a/gi_loadouts/data/weap/bows/pdtr.py
+++ b/gi_loadouts/data/weap/bows/pdtr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Predator(Bow):
diff --git a/gi_loadouts/data/weap/bows/prsr.py b/gi_loadouts/data/weap/bows/prsr.py
index 59981e0b..3ed63806 100644
--- a/gi_loadouts/data/weap/bows/prsr.py
+++ b/gi_loadouts/data/weap/bows/prsr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PolarStar(Bow):
diff --git a/gi_loadouts/data/weap/bows/ptct.py b/gi_loadouts/data/weap/bows/ptct.py
index 0fe501fc..78fae67b 100644
--- a/gi_loadouts/data/weap/bows/ptct.py
+++ b/gi_loadouts/data/weap/bows/ptct.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PrototypeCrescent(Bow):
diff --git a/gi_loadouts/data/weap/bows/rcbw.py b/gi_loadouts/data/weap/bows/rcbw.py
index f77b15f3..82aef44e 100644
--- a/gi_loadouts/data/weap/bows/rcbw.py
+++ b/gi_loadouts/data/weap/bows/rcbw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RecurveBow(Bow):
diff --git a/gi_loadouts/data/weap/bows/rnge.py b/gi_loadouts/data/weap/bows/rnge.py
index 451b3bee..0adbdd50 100644
--- a/gi_loadouts/data/weap/bows/rnge.py
+++ b/gi_loadouts/data/weap/bows/rnge.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RangeGauge(Bow):
diff --git a/gi_loadouts/data/weap/bows/rust.py b/gi_loadouts/data/weap/bows/rust.py
index 6e338436..8f87b57f 100644
--- a/gi_loadouts/data/weap/bows/rust.py
+++ b/gi_loadouts/data/weap/bows/rust.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Rust(Bow):
diff --git a/gi_loadouts/data/weap/bows/rvbw.py b/gi_loadouts/data/weap/bows/rvbw.py
index 34488214..6080cf9c 100644
--- a/gi_loadouts/data/weap/bows/rvbw.py
+++ b/gi_loadouts/data/weap/bows/rvbw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RavenBow(Bow):
diff --git a/gi_loadouts/data/weap/bows/rybw.py b/gi_loadouts/data/weap/bows/rybw.py
index 83e146da..77459d67 100644
--- a/gi_loadouts/data/weap/bows/rybw.py
+++ b/gi_loadouts/data/weap/bows/rybw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RoyalBow(Bow):
diff --git a/gi_loadouts/data/weap/bows/sfbw.py b/gi_loadouts/data/weap/bows/sfbw.py
index 590d1611..19a94974 100644
--- a/gi_loadouts/data/weap/bows/sfbw.py
+++ b/gi_loadouts/data/weap/bows/sfbw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SacrificialBow(Bow):
diff --git a/gi_loadouts/data/weap/bows/sgst.py b/gi_loadouts/data/weap/bows/sgst.py
index 3a91f44d..7535e28a 100644
--- a/gi_loadouts/data/weap/bows/sgst.py
+++ b/gi_loadouts/data/weap/bows/sgst.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Slingshot(Bow):
diff --git a/gi_loadouts/data/weap/bows/shbw.py b/gi_loadouts/data/weap/bows/shbw.py
index 3d4d0aed..e215d911 100644
--- a/gi_loadouts/data/weap/bows/shbw.py
+++ b/gi_loadouts/data/weap/bows/shbw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SeasonedHuntersBow(Bow):
diff --git a/gi_loadouts/data/weap/bows/sobs.py b/gi_loadouts/data/weap/bows/sobs.py
index 3b72f681..6ba234b2 100644
--- a/gi_loadouts/data/weap/bows/sobs.py
+++ b/gi_loadouts/data/weap/bows/sobs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ScionoftheBlazingSun(Bow):
diff --git a/gi_loadouts/data/weap/bows/sosn.py b/gi_loadouts/data/weap/bows/sosn.py
index 43fd3e04..dc20789f 100644
--- a/gi_loadouts/data/weap/bows/sosn.py
+++ b/gi_loadouts/data/weap/bows/sosn.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SongofStillness(Bow):
diff --git a/gi_loadouts/data/weap/bows/sshs.py b/gi_loadouts/data/weap/bows/sshs.py
index 585c64f4..93ec0caa 100644
--- a/gi_loadouts/data/weap/bows/sshs.py
+++ b/gi_loadouts/data/weap/bows/sshs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SilvershowerHeartstrings(Bow):
diff --git a/gi_loadouts/data/weap/bows/ssoh.py b/gi_loadouts/data/weap/bows/ssoh.py
index 4fb9e407..c0ef2832 100644
--- a/gi_loadouts/data/weap/bows/ssoh.py
+++ b/gi_loadouts/data/weap/bows/ssoh.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SharpshootersOath(Bow):
diff --git a/gi_loadouts/data/weap/bows/swhp.py b/gi_loadouts/data/weap/bows/swhp.py
index 2f21c2da..3d4470c1 100644
--- a/gi_loadouts/data/weap/bows/swhp.py
+++ b/gi_loadouts/data/weap/bows/swhp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SkywardHarp(Bow):
diff --git a/gi_loadouts/data/weap/bows/tdpe.py b/gi_loadouts/data/weap/bows/tdpe.py
index 58eb7784..b881f22e 100644
--- a/gi_loadouts/data/weap/bows/tdpe.py
+++ b/gi_loadouts/data/weap/bows/tdpe.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ThunderingPulse(Bow):
diff --git a/gi_loadouts/data/weap/bows/tfgm.py b/gi_loadouts/data/weap/bows/tfgm.py
index 804db618..76910fb8 100644
--- a/gi_loadouts/data/weap/bows/tfgm.py
+++ b/gi_loadouts/data/weap/bows/tfgm.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheFirstGreatMagic(Bow):
diff --git a/gi_loadouts/data/weap/bows/tsgs.py b/gi_loadouts/data/weap/bows/tsgs.py
index 1e6e9fec..d0e97a3a 100644
--- a/gi_loadouts/data/weap/bows/tsgs.py
+++ b/gi_loadouts/data/weap/bows/tsgs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheStringless(Bow):
diff --git a/gi_loadouts/data/weap/bows/tvsh.py b/gi_loadouts/data/weap/bows/tvsh.py
index c2cd9622..e349d7fe 100644
--- a/gi_loadouts/data/weap/bows/tvsh.py
+++ b/gi_loadouts/data/weap/bows/tvsh.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheViridescentHunt(Bow):
diff --git a/gi_loadouts/data/weap/bows/wboe.py b/gi_loadouts/data/weap/bows/wboe.py
index 2e74fc66..ab4fbcec 100644
--- a/gi_loadouts/data/weap/bows/wboe.py
+++ b/gi_loadouts/data/weap/bows/wboe.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Bow, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WindblumeOde(Bow):
diff --git a/gi_loadouts/data/weap/catalysts/__init__.py b/gi_loadouts/data/weap/catalysts/__init__.py
index 2a1c8c06..deae8a49 100644
--- a/gi_loadouts/data/weap/catalysts/__init__.py
+++ b/gi_loadouts/data/weap/catalysts/__init__.py
@@ -42,44 +42,44 @@
 )
 
 CatalystsDict = {
-    aptn.ApprenticesNotes().name: aptn.ApprenticesNotes(),
-    agdh.AshGravenDrinkingHorn().name: agdh.AshGravenDrinkingHorn(),
-    atfd.AThousandFloatingDreams().name: atfd.AThousandFloatingDreams(),
-    bobb.BalladoftheBoundlessBlue().name: bobb.BalladoftheBoundlessBlue(),
-    bcag.BlackcliffAgate().name: bcag.BlackcliffAgate(),
-    cfsv.CashflowSupervision().name: cfsv.CashflowSupervision(),
-    cegc.CranesEchoingCall().name: cegc.CranesEchoingCall(),
-    ddts.DodocoTales().name: ddts.DodocoTales(),
-    edob.EmeraldOrb().name: edob.EmeraldOrb(),
-    elmg.EverlastingMoonglow().name: elmg.EverlastingMoonglow(),
-    eypt.EyeofPerception().name: eypt.EyeofPerception(),
-    fvcx.FavoniusCodex().name: fvcx.FavoniusCodex(),
-    fwpr.FlowingPurity().name: fwpr.FlowingPurity(),
-    ftbr.Frostbearer().name: ftbr.Frostbearer(),
-    foff.FruitofFulfillment().name: foff.FruitofFulfillment(),
-    hsrg.HakushinRing().name: hsrg.HakushinRing(),
-    jfsd.JadefallsSplendor().name: jfsd.JadefallsSplendor(),
-    kgvy.KagurasVerity().name: kgvy.KagurasVerity(),
-    lpsw.LostPrayertotheSacredWinds().name: lpsw.LostPrayertotheSacredWinds(),
-    mcge.MagicGuide().name: mcge.MagicGuide(),
-    mama.MappaMare().name: mama.MappaMare(),
-    mmdt.MemoryofDust().name: mmdt.MemoryofDust(),
-    osee.OathswornEye().name: osee.OathswornEye(),
-    owsy.OtherworldlyStory().name: owsy.OtherworldlyStory(),
-    pkgm.PocketGrimoire().name: pkgm.PocketGrimoire(),
-    ptab.PrototypeAmber().name: ptab.PrototypeAmber(),
-    roye.RingofYaxche().name: roye.RingofYaxche(),
-    rygm.RoyalGrimoire().name: rygm.RoyalGrimoire(),
-    sffm.SacrificialFragments().name: sffm.SacrificialFragments(),
-    sfje.SacrificialJade().name: sfje.SacrificialJade(),
-    swas.SkywardAtlas().name: swas.SkywardAtlas(),
-    slpl.SolarPearl().name: slpl.SolarPearl(),
-    sfup.SurfsUp().name: sfup.SurfsUp(),
-    twsh.TheWidsith().name: twsh.TheWidsith(),
-    ttds.ThrillingTalesofDragonSlayers().name: ttds.ThrillingTalesofDragonSlayers(),
-    toef.TomeoftheEternalFlow().name: toef.TomeoftheEternalFlow(),
-    ttrb.TulaytullahsRemembrance().name: ttrb.TulaytullahsRemembrance(),
-    twnp.TwinNephrite().name: twnp.TwinNephrite(),
-    wdes.WanderingEvenstar().name: wdes.WanderingEvenstar(),
-    wnsg.WineandSong().name: wnsg.WineandSong()
+    "Apprentice's Notes": aptn.ApprenticesNotes,
+    "Ash-Graven Drinking Horn": agdh.AshGravenDrinkingHorn,
+    "A Thousand Floating Dreams": atfd.AThousandFloatingDreams,
+    "Ballad of the Boundless Blue": bobb.BalladoftheBoundlessBlue,
+    "Blackcliff Agate": bcag.BlackcliffAgate,
+    "Cashflow Supervision": cfsv.CashflowSupervision,
+    "Crane's Echoing Call": cegc.CranesEchoingCall,
+    "Dodoco Tales": ddts.DodocoTales,
+    "Emerald Orb": edob.EmeraldOrb,
+    "Everlasting Moonglow": elmg.EverlastingMoonglow,
+    "Eye of Perception": eypt.EyeofPerception,
+    "Favonius Codex": fvcx.FavoniusCodex,
+    "Flowing Purity": fwpr.FlowingPurity,
+    "Frostbearer": ftbr.Frostbearer,
+    "Fruit of Fulfillment": foff.FruitofFulfillment,
+    "Hakushin Ring": hsrg.HakushinRing,
+    "Jadefall's Splendor": jfsd.JadefallsSplendor,
+    "Kagura's Verity": kgvy.KagurasVerity,
+    "Lost Prayer to the Sacred Winds": lpsw.LostPrayertotheSacredWinds,
+    "Magic Guide": mcge.MagicGuide,
+    "Mappa Mare": mama.MappaMare,
+    "Memory of Dust": mmdt.MemoryofDust,
+    "Oathsworn Eye": osee.OathswornEye,
+    "Otherworldly Story": owsy.OtherworldlyStory,
+    "Pocket Grimoire": pkgm.PocketGrimoire,
+    "Prototype Amber": ptab.PrototypeAmber,
+    "Ring of Yaxche": roye.RingofYaxche,
+    "Royal Grimoire": rygm.RoyalGrimoire,
+    "Sacrificial Fragments": sffm.SacrificialFragments,
+    "Sacrificial Jade": sfje.SacrificialJade,
+    "Skyward Atlas": swas.SkywardAtlas,
+    "Solar Pearl": slpl.SolarPearl,
+    "Surf's Up": sfup.SurfsUp,
+    "The Widsith": twsh.TheWidsith,
+    "Thrilling Tales of Dragon Slayers": ttds.ThrillingTalesofDragonSlayers,
+    "Tome of the Eternal Flow": toef.TomeoftheEternalFlow,
+    "Tulaytullah's Remembrance": ttrb.TulaytullahsRemembrance,
+    "Twin Nephrite": twnp.TwinNephrite,
+    "Wandering Evenstar": wdes.WanderingEvenstar,
+    "Wine and Song": wnsg.WineandSong
 }
diff --git a/gi_loadouts/data/weap/catalysts/agdh.py b/gi_loadouts/data/weap/catalysts/agdh.py
index 9f436114..8fc7714c 100644
--- a/gi_loadouts/data/weap/catalysts/agdh.py
+++ b/gi_loadouts/data/weap/catalysts/agdh.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class AshGravenDrinkingHorn(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/aptn.py b/gi_loadouts/data/weap/catalysts/aptn.py
index 59c51385..29b934f1 100644
--- a/gi_loadouts/data/weap/catalysts/aptn.py
+++ b/gi_loadouts/data/weap/catalysts/aptn.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ApprenticesNotes(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/atfd.py b/gi_loadouts/data/weap/catalysts/atfd.py
index 0d9a9743..a00bb62a 100644
--- a/gi_loadouts/data/weap/catalysts/atfd.py
+++ b/gi_loadouts/data/weap/catalysts/atfd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class AThousandFloatingDreams(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/bcag.py b/gi_loadouts/data/weap/catalysts/bcag.py
index 4855788d..bb9587cf 100644
--- a/gi_loadouts/data/weap/catalysts/bcag.py
+++ b/gi_loadouts/data/weap/catalysts/bcag.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BlackcliffAgate(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/bobb.py b/gi_loadouts/data/weap/catalysts/bobb.py
index 5a938b51..ab57903d 100644
--- a/gi_loadouts/data/weap/catalysts/bobb.py
+++ b/gi_loadouts/data/weap/catalysts/bobb.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BalladoftheBoundlessBlue(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/cegc.py b/gi_loadouts/data/weap/catalysts/cegc.py
index 33396925..82ff131c 100644
--- a/gi_loadouts/data/weap/catalysts/cegc.py
+++ b/gi_loadouts/data/weap/catalysts/cegc.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class CranesEchoingCall(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/cfsv.py b/gi_loadouts/data/weap/catalysts/cfsv.py
index 62e5f6f2..3ec298e0 100644
--- a/gi_loadouts/data/weap/catalysts/cfsv.py
+++ b/gi_loadouts/data/weap/catalysts/cfsv.py
@@ -2,13 +2,13 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class CashflowSupervision(Catalyst):
     name: str = "Cashflow Supervision"
     seco_stat: WeaponStat = WeaponStat(stat_name=WeaponStatType.critical_rate_perc, stat_data=4.8)
-    tier: Tier = Tier.Tier_2
+    tier: Tier = Tier.Tier_3
     rare: Rare = Rare.Star_5
     refi_name: str = "Golden Blood-Tide"
     refi_list: List[str] = [
diff --git a/gi_loadouts/data/weap/catalysts/ddts.py b/gi_loadouts/data/weap/catalysts/ddts.py
index 99978232..f00da95a 100644
--- a/gi_loadouts/data/weap/catalysts/ddts.py
+++ b/gi_loadouts/data/weap/catalysts/ddts.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class DodocoTales(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/edob.py b/gi_loadouts/data/weap/catalysts/edob.py
index d2f9cc38..95d9348e 100644
--- a/gi_loadouts/data/weap/catalysts/edob.py
+++ b/gi_loadouts/data/weap/catalysts/edob.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class EmeraldOrb(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/elmg.py b/gi_loadouts/data/weap/catalysts/elmg.py
index 2d36ef58..409dd378 100644
--- a/gi_loadouts/data/weap/catalysts/elmg.py
+++ b/gi_loadouts/data/weap/catalysts/elmg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class EverlastingMoonglow(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/eypt.py b/gi_loadouts/data/weap/catalysts/eypt.py
index d3eed1a4..e65d73db 100644
--- a/gi_loadouts/data/weap/catalysts/eypt.py
+++ b/gi_loadouts/data/weap/catalysts/eypt.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class EyeofPerception(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/foff.py b/gi_loadouts/data/weap/catalysts/foff.py
index 9869cbff..6aef9d6c 100644
--- a/gi_loadouts/data/weap/catalysts/foff.py
+++ b/gi_loadouts/data/weap/catalysts/foff.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FruitofFulfillment(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/ftbr.py b/gi_loadouts/data/weap/catalysts/ftbr.py
index 1d3d4e64..07729c63 100644
--- a/gi_loadouts/data/weap/catalysts/ftbr.py
+++ b/gi_loadouts/data/weap/catalysts/ftbr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Frostbearer(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/fvcx.py b/gi_loadouts/data/weap/catalysts/fvcx.py
index b66e55f8..e7dc8992 100644
--- a/gi_loadouts/data/weap/catalysts/fvcx.py
+++ b/gi_loadouts/data/weap/catalysts/fvcx.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FavoniusCodex(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/fwpr.py b/gi_loadouts/data/weap/catalysts/fwpr.py
index bdecff13..fe10cf1a 100644
--- a/gi_loadouts/data/weap/catalysts/fwpr.py
+++ b/gi_loadouts/data/weap/catalysts/fwpr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FlowingPurity(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/hsrg.py b/gi_loadouts/data/weap/catalysts/hsrg.py
index 5d49c7de..e00efdbe 100644
--- a/gi_loadouts/data/weap/catalysts/hsrg.py
+++ b/gi_loadouts/data/weap/catalysts/hsrg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class HakushinRing(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/jfsd.py b/gi_loadouts/data/weap/catalysts/jfsd.py
index fefad6f0..007302cd 100644
--- a/gi_loadouts/data/weap/catalysts/jfsd.py
+++ b/gi_loadouts/data/weap/catalysts/jfsd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class JadefallsSplendor(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/kgvy.py b/gi_loadouts/data/weap/catalysts/kgvy.py
index 5990b576..8d952059 100644
--- a/gi_loadouts/data/weap/catalysts/kgvy.py
+++ b/gi_loadouts/data/weap/catalysts/kgvy.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class KagurasVerity(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/lpsw.py b/gi_loadouts/data/weap/catalysts/lpsw.py
index 8a6ceef0..db064a2b 100644
--- a/gi_loadouts/data/weap/catalysts/lpsw.py
+++ b/gi_loadouts/data/weap/catalysts/lpsw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class LostPrayertotheSacredWinds(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/mama.py b/gi_loadouts/data/weap/catalysts/mama.py
index 924491d5..44d80178 100644
--- a/gi_loadouts/data/weap/catalysts/mama.py
+++ b/gi_loadouts/data/weap/catalysts/mama.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MappaMare(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/mcge.py b/gi_loadouts/data/weap/catalysts/mcge.py
index 2a8918a8..ecc83b4c 100644
--- a/gi_loadouts/data/weap/catalysts/mcge.py
+++ b/gi_loadouts/data/weap/catalysts/mcge.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MagicGuide(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/mmdt.py b/gi_loadouts/data/weap/catalysts/mmdt.py
index 06171468..9a1b6365 100644
--- a/gi_loadouts/data/weap/catalysts/mmdt.py
+++ b/gi_loadouts/data/weap/catalysts/mmdt.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MemoryofDust(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/osee.py b/gi_loadouts/data/weap/catalysts/osee.py
index c356d82f..c468b7d5 100644
--- a/gi_loadouts/data/weap/catalysts/osee.py
+++ b/gi_loadouts/data/weap/catalysts/osee.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class OathswornEye(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/owsy.py b/gi_loadouts/data/weap/catalysts/owsy.py
index 827d3981..ad49e55c 100644
--- a/gi_loadouts/data/weap/catalysts/owsy.py
+++ b/gi_loadouts/data/weap/catalysts/owsy.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class OtherworldlyStory(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/pkgm.py b/gi_loadouts/data/weap/catalysts/pkgm.py
index 34580768..2cc9d0f4 100644
--- a/gi_loadouts/data/weap/catalysts/pkgm.py
+++ b/gi_loadouts/data/weap/catalysts/pkgm.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PocketGrimoire(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/ptab.py b/gi_loadouts/data/weap/catalysts/ptab.py
index 6fe214e3..c903dee6 100644
--- a/gi_loadouts/data/weap/catalysts/ptab.py
+++ b/gi_loadouts/data/weap/catalysts/ptab.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PrototypeAmber(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/roye.py b/gi_loadouts/data/weap/catalysts/roye.py
index c0ff71f8..15ac2056 100644
--- a/gi_loadouts/data/weap/catalysts/roye.py
+++ b/gi_loadouts/data/weap/catalysts/roye.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RingofYaxche(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/rygm.py b/gi_loadouts/data/weap/catalysts/rygm.py
index 5f8003c8..f4aa7d6c 100644
--- a/gi_loadouts/data/weap/catalysts/rygm.py
+++ b/gi_loadouts/data/weap/catalysts/rygm.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RoyalGrimoire(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/sffm.py b/gi_loadouts/data/weap/catalysts/sffm.py
index 2b298980..1b87602e 100644
--- a/gi_loadouts/data/weap/catalysts/sffm.py
+++ b/gi_loadouts/data/weap/catalysts/sffm.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SacrificialFragments(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/sfje.py b/gi_loadouts/data/weap/catalysts/sfje.py
index f3ab9b37..8540c5b1 100644
--- a/gi_loadouts/data/weap/catalysts/sfje.py
+++ b/gi_loadouts/data/weap/catalysts/sfje.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SacrificialJade(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/sfup.py b/gi_loadouts/data/weap/catalysts/sfup.py
index 06f604a8..a5ac0f38 100644
--- a/gi_loadouts/data/weap/catalysts/sfup.py
+++ b/gi_loadouts/data/weap/catalysts/sfup.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SurfsUp(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/slpl.py b/gi_loadouts/data/weap/catalysts/slpl.py
index 61c7d2e6..3d65b455 100644
--- a/gi_loadouts/data/weap/catalysts/slpl.py
+++ b/gi_loadouts/data/weap/catalysts/slpl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SolarPearl(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/swas.py b/gi_loadouts/data/weap/catalysts/swas.py
index 01c5e4a1..4f04b04a 100644
--- a/gi_loadouts/data/weap/catalysts/swas.py
+++ b/gi_loadouts/data/weap/catalysts/swas.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SkywardAtlas(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/toef.py b/gi_loadouts/data/weap/catalysts/toef.py
index c73a6352..bfb26ed3 100644
--- a/gi_loadouts/data/weap/catalysts/toef.py
+++ b/gi_loadouts/data/weap/catalysts/toef.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TomeoftheEternalFlow(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/ttds.py b/gi_loadouts/data/weap/catalysts/ttds.py
index e17710fb..891a744c 100644
--- a/gi_loadouts/data/weap/catalysts/ttds.py
+++ b/gi_loadouts/data/weap/catalysts/ttds.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ThrillingTalesofDragonSlayers(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/ttrb.py b/gi_loadouts/data/weap/catalysts/ttrb.py
index 16c17257..2e6650be 100644
--- a/gi_loadouts/data/weap/catalysts/ttrb.py
+++ b/gi_loadouts/data/weap/catalysts/ttrb.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TulaytullahsRemembrance(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/twnp.py b/gi_loadouts/data/weap/catalysts/twnp.py
index 3aa54866..53bfa177 100644
--- a/gi_loadouts/data/weap/catalysts/twnp.py
+++ b/gi_loadouts/data/weap/catalysts/twnp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TwinNephrite(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/twsh.py b/gi_loadouts/data/weap/catalysts/twsh.py
index bd096512..19ce2d95 100644
--- a/gi_loadouts/data/weap/catalysts/twsh.py
+++ b/gi_loadouts/data/weap/catalysts/twsh.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheWidsith(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/wdes.py b/gi_loadouts/data/weap/catalysts/wdes.py
index 6250e63b..957a0750 100644
--- a/gi_loadouts/data/weap/catalysts/wdes.py
+++ b/gi_loadouts/data/weap/catalysts/wdes.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WanderingEvenstar(Catalyst):
diff --git a/gi_loadouts/data/weap/catalysts/wnsg.py b/gi_loadouts/data/weap/catalysts/wnsg.py
index 223356b5..b499fd07 100644
--- a/gi_loadouts/data/weap/catalysts/wnsg.py
+++ b/gi_loadouts/data/weap/catalysts/wnsg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Catalyst, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WineandSong(Catalyst):
diff --git a/gi_loadouts/data/weap/claymores/__init__.py b/gi_loadouts/data/weap/claymores/__init__.py
index c9b446cf..9acb2c8a 100644
--- a/gi_loadouts/data/weap/claymores/__init__.py
+++ b/gi_loadouts/data/weap/claymores/__init__.py
@@ -37,41 +37,41 @@
     wtbd,
 )
 
-ClaymoreDict = {
-    akmr.Akuoumaru().name: akmr.Akuoumaru(),
-    bors.BeaconOfTheReedSea().name: bors.BeaconOfTheReedSea(),
-    bcsh.BlackcliffSlasher().name: bcsh.BlackcliffSlasher(),
-    btgs.BloodtaintedGreatsword().name: btgs.BloodtaintedGreatsword(),
-    dbcl.DebateClub().name: dbcl.DebateClub(),
-    ehsr.EarthShaker().name: ehsr.EarthShaker(),
-    fngs.FavoniusGreatsword().name: fngs.FavoniusGreatsword(),
-    fosd.FerrousShadow().name: fosd.FerrousShadow(),
-    frrg.ForestRegalia().name: frrg.ForestRegalia(),
-    kknm.KatsuragikiriNagamasa().name: kknm.KatsuragikiriNagamasa(),
-    libd.LithicBlade().name: libd.LithicBlade(),
-    lrsl.LuxuriousSeaLord().name: lrsl.LuxuriousSeaLord(),
-    mlfw.MailedFlower().name: mlfw.MailedFlower(),
-    miam.MakhairaAquamarine().name: miam.MakhairaAquamarine(),
-    omsp.OldMercsPal().name: omsp.OldMercsPal(),
-    ptps.PortablePowerSaw().name: ptps.PortablePowerSaw(),
-    ptar.PrototypeArchaic().name: ptar.PrototypeArchaic(),
-    rnsh.Rainslasher().name: rnsh.Rainslasher(),
-    rhst.RedhornStonethresher().name: rhst.RedhornStonethresher(),
-    rygs.RoyalGreatsword().name: rygs.RoyalGreatsword(),
-    sfgs.SacrificialGreatsword().name: sfgs.SacrificialGreatsword(),
-    spsp.SerpentSpine().name: spsp.SerpentSpine(),
-    srgs.SkyriderGreatsword().name: srgs.SkyriderGreatsword(),
-    swpr.SkywardPride().name: swpr.SkywardPride(),
-    stss.SnowTombedStarsilver().name: stss.SnowTombedStarsilver(),
-    sobp.SongofBrokenPines().name: sobp.SongofBrokenPines(),
-    tgsk.TalkingStick().name: tgsk.TalkingStick(),
-    tbll.TheBell().name: tbll.TheBell(),
-    tufg.TheUnforged().name: tufg.TheUnforged(),
-    tdsd.TidalShadow().name: tdsd.TidalShadow(),
-    uoms.UltimateOverlordsMegaMagicSword().name: uoms.UltimateOverlordsMegaMagicSword(),
-    vrdt.Verdict().name: vrdt.Verdict(),
-    wrgs.WasterGreatsword().name: wrgs.WasterGreatsword(),
-    wtbd.Whiteblind().name: wtbd.Whiteblind(),
-    wigs.WhiteIronGreatsword().name: wigs.WhiteIronGreatsword(),
-    wfgs.WolfsGravestone().name: wfgs.WolfsGravestone(),
+ClaymoresDict = {
+    "Akuoumaru": akmr.Akuoumaru,
+    "Beacon of the Reed Sea": bors.BeaconOfTheReedSea,
+    "Blackcliff Slasher": bcsh.BlackcliffSlasher,
+    "Bloodtainted Greatsword": btgs.BloodtaintedGreatsword,
+    "Debate Club": dbcl.DebateClub,
+    "Earth Shaker": ehsr.EarthShaker,
+    "Favonius Greatsword": fngs.FavoniusGreatsword,
+    "Ferrous Shadow": fosd.FerrousShadow,
+    "Forest Regalia": frrg.ForestRegalia,
+    "Katsuragikiri Nagamasa": kknm.KatsuragikiriNagamasa,
+    "Lithic Blade": libd.LithicBlade,
+    "Luxurious Sea-Lord": lrsl.LuxuriousSeaLord,
+    "Mailed Flower": mlfw.MailedFlower,
+    "Makhaira Aquamarine": miam.MakhairaAquamarine,
+    "Old Merc's Pal": omsp.OldMercsPal,
+    "Portable Power Saw": ptps.PortablePowerSaw,
+    "Prototype Archaic": ptar.PrototypeArchaic,
+    "Rainslasher": rnsh.Rainslasher,
+    "Redhorn Stonethresher": rhst.RedhornStonethresher,
+    "Royal Greatsword": rygs.RoyalGreatsword,
+    "Sacrificial Greatsword": sfgs.SacrificialGreatsword,
+    "Serpent Spine": spsp.SerpentSpine,
+    "Skyrider Greatsword": srgs.SkyriderGreatsword,
+    "Skyward Pride": swpr.SkywardPride,
+    "Snow-Tombed Starsilver": stss.SnowTombedStarsilver,
+    "Song of Broken Pines": sobp.SongofBrokenPines,
+    "Talking Stick": tgsk.TalkingStick,
+    "The Bell": tbll.TheBell,
+    "The Unforged": tufg.TheUnforged,
+    "Tidal Shadow": tdsd.TidalShadow,
+    "\"Ultimate Overlord's Mega Magic Sword\"": uoms.UltimateOverlordsMegaMagicSword,
+    "Verdict": vrdt.Verdict,
+    "Waster Greatsword": wrgs.WasterGreatsword,
+    "Whiteblind": wtbd.Whiteblind,
+    "White Iron Greatsword": wigs.WhiteIronGreatsword,
+    "Wolf's Gravestone": wfgs.WolfsGravestone,
 }
diff --git a/gi_loadouts/data/weap/claymores/akmr.py b/gi_loadouts/data/weap/claymores/akmr.py
index 6a3f1034..a7b4be0f 100644
--- a/gi_loadouts/data/weap/claymores/akmr.py
+++ b/gi_loadouts/data/weap/claymores/akmr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Akuoumaru(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/bcsh.py b/gi_loadouts/data/weap/claymores/bcsh.py
index 28c2cb1f..7bcac925 100644
--- a/gi_loadouts/data/weap/claymores/bcsh.py
+++ b/gi_loadouts/data/weap/claymores/bcsh.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BlackcliffSlasher(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/bors.py b/gi_loadouts/data/weap/claymores/bors.py
index 94205569..ccf670f6 100644
--- a/gi_loadouts/data/weap/claymores/bors.py
+++ b/gi_loadouts/data/weap/claymores/bors.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BeaconOfTheReedSea(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/btgs.py b/gi_loadouts/data/weap/claymores/btgs.py
index 89451d91..e2684a90 100644
--- a/gi_loadouts/data/weap/claymores/btgs.py
+++ b/gi_loadouts/data/weap/claymores/btgs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BloodtaintedGreatsword(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/dbcl.py b/gi_loadouts/data/weap/claymores/dbcl.py
index 7f5175f0..381c7863 100644
--- a/gi_loadouts/data/weap/claymores/dbcl.py
+++ b/gi_loadouts/data/weap/claymores/dbcl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class DebateClub(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/ehsr.py b/gi_loadouts/data/weap/claymores/ehsr.py
index 6857b06c..99a56622 100644
--- a/gi_loadouts/data/weap/claymores/ehsr.py
+++ b/gi_loadouts/data/weap/claymores/ehsr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class EarthShaker(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/fngs.py b/gi_loadouts/data/weap/claymores/fngs.py
index 323e964b..e276b42b 100644
--- a/gi_loadouts/data/weap/claymores/fngs.py
+++ b/gi_loadouts/data/weap/claymores/fngs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FavoniusGreatsword(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/fosd.py b/gi_loadouts/data/weap/claymores/fosd.py
index 4bfce28b..9a1f2c79 100644
--- a/gi_loadouts/data/weap/claymores/fosd.py
+++ b/gi_loadouts/data/weap/claymores/fosd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FerrousShadow(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/frrg.py b/gi_loadouts/data/weap/claymores/frrg.py
index 9a6266cd..38cd96be 100644
--- a/gi_loadouts/data/weap/claymores/frrg.py
+++ b/gi_loadouts/data/weap/claymores/frrg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ForestRegalia(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/kknm.py b/gi_loadouts/data/weap/claymores/kknm.py
index a99fc6a0..6f00ab53 100644
--- a/gi_loadouts/data/weap/claymores/kknm.py
+++ b/gi_loadouts/data/weap/claymores/kknm.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class KatsuragikiriNagamasa(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/libd.py b/gi_loadouts/data/weap/claymores/libd.py
index ad396728..e24f37c2 100644
--- a/gi_loadouts/data/weap/claymores/libd.py
+++ b/gi_loadouts/data/weap/claymores/libd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class LithicBlade(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/lrsl.py b/gi_loadouts/data/weap/claymores/lrsl.py
index d7274456..779e1571 100644
--- a/gi_loadouts/data/weap/claymores/lrsl.py
+++ b/gi_loadouts/data/weap/claymores/lrsl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class LuxuriousSeaLord(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/miam.py b/gi_loadouts/data/weap/claymores/miam.py
index 7bc25d8c..b57ae402 100644
--- a/gi_loadouts/data/weap/claymores/miam.py
+++ b/gi_loadouts/data/weap/claymores/miam.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 """
 Dynamic calculation not implemented
diff --git a/gi_loadouts/data/weap/claymores/mlfw.py b/gi_loadouts/data/weap/claymores/mlfw.py
index 28d8f977..f7c6af85 100644
--- a/gi_loadouts/data/weap/claymores/mlfw.py
+++ b/gi_loadouts/data/weap/claymores/mlfw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MailedFlower(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/omsp.py b/gi_loadouts/data/weap/claymores/omsp.py
index 1fcec94b..59e8271c 100644
--- a/gi_loadouts/data/weap/claymores/omsp.py
+++ b/gi_loadouts/data/weap/claymores/omsp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class OldMercsPal(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/ptar.py b/gi_loadouts/data/weap/claymores/ptar.py
index 394be0fe..f7613649 100644
--- a/gi_loadouts/data/weap/claymores/ptar.py
+++ b/gi_loadouts/data/weap/claymores/ptar.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PrototypeArchaic(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/ptps.py b/gi_loadouts/data/weap/claymores/ptps.py
index 91e8d50a..83da9c6b 100644
--- a/gi_loadouts/data/weap/claymores/ptps.py
+++ b/gi_loadouts/data/weap/claymores/ptps.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PortablePowerSaw(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/rhst.py b/gi_loadouts/data/weap/claymores/rhst.py
index 35541e46..2a1edb5b 100644
--- a/gi_loadouts/data/weap/claymores/rhst.py
+++ b/gi_loadouts/data/weap/claymores/rhst.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RedhornStonethresher(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/rnsh.py b/gi_loadouts/data/weap/claymores/rnsh.py
index 86a84be7..47596844 100644
--- a/gi_loadouts/data/weap/claymores/rnsh.py
+++ b/gi_loadouts/data/weap/claymores/rnsh.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Rainslasher(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/rygs.py b/gi_loadouts/data/weap/claymores/rygs.py
index 7177d114..79e824ed 100644
--- a/gi_loadouts/data/weap/claymores/rygs.py
+++ b/gi_loadouts/data/weap/claymores/rygs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RoyalGreatsword(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/sfgs.py b/gi_loadouts/data/weap/claymores/sfgs.py
index 274cad73..7abd94fd 100644
--- a/gi_loadouts/data/weap/claymores/sfgs.py
+++ b/gi_loadouts/data/weap/claymores/sfgs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SacrificialGreatsword(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/sobp.py b/gi_loadouts/data/weap/claymores/sobp.py
index 3713d57d..0a5a0d73 100644
--- a/gi_loadouts/data/weap/claymores/sobp.py
+++ b/gi_loadouts/data/weap/claymores/sobp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SongofBrokenPines(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/spsp.py b/gi_loadouts/data/weap/claymores/spsp.py
index 833600cc..3cec2701 100644
--- a/gi_loadouts/data/weap/claymores/spsp.py
+++ b/gi_loadouts/data/weap/claymores/spsp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SerpentSpine(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/srgs.py b/gi_loadouts/data/weap/claymores/srgs.py
index 637dc3ee..e6df6310 100644
--- a/gi_loadouts/data/weap/claymores/srgs.py
+++ b/gi_loadouts/data/weap/claymores/srgs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SkyriderGreatsword(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/stss.py b/gi_loadouts/data/weap/claymores/stss.py
index 96f6992a..525ac35b 100644
--- a/gi_loadouts/data/weap/claymores/stss.py
+++ b/gi_loadouts/data/weap/claymores/stss.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SnowTombedStarsilver(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/swpr.py b/gi_loadouts/data/weap/claymores/swpr.py
index 10687282..2470f21c 100644
--- a/gi_loadouts/data/weap/claymores/swpr.py
+++ b/gi_loadouts/data/weap/claymores/swpr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 """
 To Do: Check if the increase DMG is DMG Bonus or something else
diff --git a/gi_loadouts/data/weap/claymores/tbll.py b/gi_loadouts/data/weap/claymores/tbll.py
index 4cf2c873..de515a03 100644
--- a/gi_loadouts/data/weap/claymores/tbll.py
+++ b/gi_loadouts/data/weap/claymores/tbll.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheBell(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/tdsd.py b/gi_loadouts/data/weap/claymores/tdsd.py
index cae77a63..96c138a2 100644
--- a/gi_loadouts/data/weap/claymores/tdsd.py
+++ b/gi_loadouts/data/weap/claymores/tdsd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TidalShadow(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/tgsk.py b/gi_loadouts/data/weap/claymores/tgsk.py
index caa0c1ce..c515307a 100644
--- a/gi_loadouts/data/weap/claymores/tgsk.py
+++ b/gi_loadouts/data/weap/claymores/tgsk.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TalkingStick(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/tufg.py b/gi_loadouts/data/weap/claymores/tufg.py
index a3a3c58b..b1ecc9da 100644
--- a/gi_loadouts/data/weap/claymores/tufg.py
+++ b/gi_loadouts/data/weap/claymores/tufg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheUnforged(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/uoms.py b/gi_loadouts/data/weap/claymores/uoms.py
index a23f34ce..aabb8358 100644
--- a/gi_loadouts/data/weap/claymores/uoms.py
+++ b/gi_loadouts/data/weap/claymores/uoms.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 #Please consider the world quest completion will increase the atk more.
 
diff --git a/gi_loadouts/data/weap/claymores/vrdt.py b/gi_loadouts/data/weap/claymores/vrdt.py
index d79197d7..1dd0ca9b 100644
--- a/gi_loadouts/data/weap/claymores/vrdt.py
+++ b/gi_loadouts/data/weap/claymores/vrdt.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Verdict(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/wfgs.py b/gi_loadouts/data/weap/claymores/wfgs.py
index 58f0294e..2cded1b9 100644
--- a/gi_loadouts/data/weap/claymores/wfgs.py
+++ b/gi_loadouts/data/weap/claymores/wfgs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WolfsGravestone(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/wigs.py b/gi_loadouts/data/weap/claymores/wigs.py
index 99f80128..f48a85dc 100644
--- a/gi_loadouts/data/weap/claymores/wigs.py
+++ b/gi_loadouts/data/weap/claymores/wigs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WhiteIronGreatsword(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/wrgs.py b/gi_loadouts/data/weap/claymores/wrgs.py
index 89a70507..8cb72f02 100644
--- a/gi_loadouts/data/weap/claymores/wrgs.py
+++ b/gi_loadouts/data/weap/claymores/wrgs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WasterGreatsword(Claymore):
diff --git a/gi_loadouts/data/weap/claymores/wtbd.py b/gi_loadouts/data/weap/claymores/wtbd.py
index 8fc75b60..c2b73295 100644
--- a/gi_loadouts/data/weap/claymores/wtbd.py
+++ b/gi_loadouts/data/weap/claymores/wtbd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Claymore, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Whiteblind(Claymore):
diff --git a/gi_loadouts/data/weap/polearms/__init__.py b/gi_loadouts/data/weap/polearms/__init__.py
index 209ca8c5..cf457fe3 100644
--- a/gi_loadouts/data/weap/polearms/__init__.py
+++ b/gi_loadouts/data/weap/polearms/__init__.py
@@ -35,37 +35,37 @@
 )
 
 PolearmsDict = {
-    botf.BalladoftheFjords().name: botf.BalladoftheFjords(),
-    bcpl.BlackcliffPole().name: bcpl.BlackcliffPole(),
-    bktl.BlackTassel().name: bktl.BlackTassel(),
-    bgpr.BeginnersProtector().name: bgpr.BeginnersProtector(),
-    cmql.CalamityQueller().name: cmql.CalamityQueller(),
-    ccpk.CrescentPike().name: ccpk.CrescentPike(),
-    cmsl.CrimsonMoonsSemblance().name: cmsl.CrimsonMoonsSemblance(),
-    dtmh.Deathmatch().name: dtmh.Deathmatch(),
-    dtds.DialoguesoftheDesertSages().name: dtds.DialoguesoftheDesertSages(),
-    dgbn.DragonsBane().name: dgbn.DragonsBane(),
-    dssr.DragonspineSpear().name: dssr.DragonspineSpear(),
-    efln.EngulfingLightning().name: efln.EngulfingLightning(),
-    fnlc.FavoniusLance().name: fnlc.FavoniusLance(),
-    fotr.FootprintoftheRainbow().name: fotr.FootprintoftheRainbow(),
-    hlbd.Halberd().name: hlbd.Halberd(),
-    inpt.IronPoint().name: inpt.IronPoint(),
-    kncs.KitainCrossSpear().name: kncs.KitainCrossSpear(),
-    lcsr.LithicSpear().name: lcsr.LithicSpear(),
-    ldeg.LumidouceElegy().name: ldeg.LumidouceElegy(),
-    mswp.MissiveWindspear().name: mswp.MissiveWindspear(),
-    mnpr.Moonpiercer().name: mnpr.Moonpiercer(),
-    pjws.PrimordialJadeWingedSpear().name: pjws.PrimordialJadeWingedSpear(),
-    pdrl.ProspectorsDrill().name: pdrl.ProspectorsDrill(),
-    ptsg.PrototypeStarglitter().name: ptsg.PrototypeStarglitter(),
-    rtrw.RightfulReward().name: rtrw.RightfulReward(),
-    rysp.RoyalSpear().name: rysp.RoyalSpear(),
-    sohm.StaffOfHoma().name: sohm.StaffOfHoma(),
-    soss.StaffOfTheScarletSands().name: soss.StaffOfTheScarletSands(),
-    swsp.SkywardSpine().name: swsp.SkywardSpine(),
-    tcth.TheCatch().name: tcth.TheCatch(),
-    vtvq.VortexVanquisher().name: vtvq.VortexVanquisher(),
-    wbfn.WavebreakersFin().name: wbfn.WavebreakersFin(),
-    wetl.WhiteTassel().name: wetl.WhiteTassel(),
+    "Ballad of the Fjords": botf.BalladoftheFjords,
+    "Blackcliff Pole": bcpl.BlackcliffPole,
+    "Black Tassel": bktl.BlackTassel,
+    "Beginner's Protector": bgpr.BeginnersProtector,
+    "Calamity Queller": cmql.CalamityQueller,
+    "Crescent Pike": ccpk.CrescentPike,
+    "Crimson Moon's Semblance": cmsl.CrimsonMoonsSemblance,
+    "Deathmatch": dtmh.Deathmatch,
+    "Dialogues of the Desert Sages": dtds.DialoguesoftheDesertSages,
+    "Dragon's Bane": dgbn.DragonsBane,
+    "Dragonspine Spear": dssr.DragonspineSpear,
+    "Engulfing Lightning": efln.EngulfingLightning,
+    "Favonius Lance": fnlc.FavoniusLance,
+    "Footprint of the Rainbow": fotr.FootprintoftheRainbow,
+    "Halberd": hlbd.Halberd,
+    "Iron Point": inpt.IronPoint,
+    "Kitain Cross Spear": kncs.KitainCrossSpear,
+    "Lithic Spear": lcsr.LithicSpear,
+    "Lumidouce Elegy": ldeg.LumidouceElegy,
+    "Missive Windspear": mswp.MissiveWindspear,
+    "Moonpiercer": mnpr.Moonpiercer,
+    "Primordial Jade Winged-Spear": pjws.PrimordialJadeWingedSpear,
+    "Prospector's Drill": pdrl.ProspectorsDrill,
+    "Prototype Starglitter": ptsg.PrototypeStarglitter,
+    "Rightful Reward": rtrw.RightfulReward,
+    "Royal Spear": rysp.RoyalSpear,
+    "Staff of Homa": sohm.StaffOfHoma,
+    "Staff of the Scarlet Sands": soss.StaffOfTheScarletSands,
+    "Skyward Spine": swsp.SkywardSpine,
+    "\"The Catch\"": tcth.TheCatch,
+    "Vortex Vanquisher": vtvq.VortexVanquisher,
+    "Wavebreaker's Fin": wbfn.WavebreakersFin,
+    "White Tassel": wetl.WhiteTassel,
 }
diff --git a/gi_loadouts/data/weap/polearms/bcpl.py b/gi_loadouts/data/weap/polearms/bcpl.py
index 8bfe0b23..61fb6478 100644
--- a/gi_loadouts/data/weap/polearms/bcpl.py
+++ b/gi_loadouts/data/weap/polearms/bcpl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BlackcliffPole(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/bgpr.py b/gi_loadouts/data/weap/polearms/bgpr.py
index dfe6b14c..aa93dd5a 100644
--- a/gi_loadouts/data/weap/polearms/bgpr.py
+++ b/gi_loadouts/data/weap/polearms/bgpr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BeginnersProtector(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/bktl.py b/gi_loadouts/data/weap/polearms/bktl.py
index cc96c537..f39e2fcb 100644
--- a/gi_loadouts/data/weap/polearms/bktl.py
+++ b/gi_loadouts/data/weap/polearms/bktl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BlackTassel(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/botf.py b/gi_loadouts/data/weap/polearms/botf.py
index af24e26f..cc57ca62 100644
--- a/gi_loadouts/data/weap/polearms/botf.py
+++ b/gi_loadouts/data/weap/polearms/botf.py
@@ -3,7 +3,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BalladoftheFjords(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/ccpk.py b/gi_loadouts/data/weap/polearms/ccpk.py
index 54fb159f..7d514add 100644
--- a/gi_loadouts/data/weap/polearms/ccpk.py
+++ b/gi_loadouts/data/weap/polearms/ccpk.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class CrescentPike(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/cmql.py b/gi_loadouts/data/weap/polearms/cmql.py
index 9b61fd2d..c63c66da 100644
--- a/gi_loadouts/data/weap/polearms/cmql.py
+++ b/gi_loadouts/data/weap/polearms/cmql.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class CalamityQueller(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/cmsl.py b/gi_loadouts/data/weap/polearms/cmsl.py
index 89a93c73..a1266792 100644
--- a/gi_loadouts/data/weap/polearms/cmsl.py
+++ b/gi_loadouts/data/weap/polearms/cmsl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class CrimsonMoonsSemblance(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/dgbn.py b/gi_loadouts/data/weap/polearms/dgbn.py
index ce0c0383..b662f6c2 100644
--- a/gi_loadouts/data/weap/polearms/dgbn.py
+++ b/gi_loadouts/data/weap/polearms/dgbn.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class DragonsBane(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/dssr.py b/gi_loadouts/data/weap/polearms/dssr.py
index 5e1d3f85..0993b4e3 100644
--- a/gi_loadouts/data/weap/polearms/dssr.py
+++ b/gi_loadouts/data/weap/polearms/dssr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class DragonspineSpear(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/dtds.py b/gi_loadouts/data/weap/polearms/dtds.py
index 40996b83..d5f5d9aa 100644
--- a/gi_loadouts/data/weap/polearms/dtds.py
+++ b/gi_loadouts/data/weap/polearms/dtds.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class DialoguesoftheDesertSages(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/dtmh.py b/gi_loadouts/data/weap/polearms/dtmh.py
index 2940a70b..bf3ce358 100644
--- a/gi_loadouts/data/weap/polearms/dtmh.py
+++ b/gi_loadouts/data/weap/polearms/dtmh.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Deathmatch(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/efln.py b/gi_loadouts/data/weap/polearms/efln.py
index ce037386..4e7c920d 100644
--- a/gi_loadouts/data/weap/polearms/efln.py
+++ b/gi_loadouts/data/weap/polearms/efln.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 """
 Dynamic calculation not implemented
diff --git a/gi_loadouts/data/weap/polearms/fnlc.py b/gi_loadouts/data/weap/polearms/fnlc.py
index 4b03926a..4570a771 100644
--- a/gi_loadouts/data/weap/polearms/fnlc.py
+++ b/gi_loadouts/data/weap/polearms/fnlc.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FavoniusLance(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/fotr.py b/gi_loadouts/data/weap/polearms/fotr.py
index 4c0350bc..b8d62d95 100644
--- a/gi_loadouts/data/weap/polearms/fotr.py
+++ b/gi_loadouts/data/weap/polearms/fotr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FootprintoftheRainbow(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/hlbd.py b/gi_loadouts/data/weap/polearms/hlbd.py
index e322ce89..72e5ee82 100644
--- a/gi_loadouts/data/weap/polearms/hlbd.py
+++ b/gi_loadouts/data/weap/polearms/hlbd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Halberd(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/inpt.py b/gi_loadouts/data/weap/polearms/inpt.py
index 348997fe..d5616c4e 100644
--- a/gi_loadouts/data/weap/polearms/inpt.py
+++ b/gi_loadouts/data/weap/polearms/inpt.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class IronPoint(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/kncs.py b/gi_loadouts/data/weap/polearms/kncs.py
index 1256a756..30e5b788 100644
--- a/gi_loadouts/data/weap/polearms/kncs.py
+++ b/gi_loadouts/data/weap/polearms/kncs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class KitainCrossSpear(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/lcsr.py b/gi_loadouts/data/weap/polearms/lcsr.py
index fcc9c4ab..5e5424d5 100644
--- a/gi_loadouts/data/weap/polearms/lcsr.py
+++ b/gi_loadouts/data/weap/polearms/lcsr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class LithicSpear(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/ldeg.py b/gi_loadouts/data/weap/polearms/ldeg.py
index f63cdce8..da571e63 100644
--- a/gi_loadouts/data/weap/polearms/ldeg.py
+++ b/gi_loadouts/data/weap/polearms/ldeg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class LumidouceElegy(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/mnpr.py b/gi_loadouts/data/weap/polearms/mnpr.py
index 8d0acc5b..8e24055c 100644
--- a/gi_loadouts/data/weap/polearms/mnpr.py
+++ b/gi_loadouts/data/weap/polearms/mnpr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Moonpiercer(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/mswp.py b/gi_loadouts/data/weap/polearms/mswp.py
index d3c4f582..a2350450 100644
--- a/gi_loadouts/data/weap/polearms/mswp.py
+++ b/gi_loadouts/data/weap/polearms/mswp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MissiveWindspear(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/pdrl.py b/gi_loadouts/data/weap/polearms/pdrl.py
index 3ee75f11..c81e3abd 100644
--- a/gi_loadouts/data/weap/polearms/pdrl.py
+++ b/gi_loadouts/data/weap/polearms/pdrl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ProspectorsDrill(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/pjws.py b/gi_loadouts/data/weap/polearms/pjws.py
index 0e792757..b84e20a3 100644
--- a/gi_loadouts/data/weap/polearms/pjws.py
+++ b/gi_loadouts/data/weap/polearms/pjws.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PrimordialJadeWingedSpear(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/ptsg.py b/gi_loadouts/data/weap/polearms/ptsg.py
index 88c5c8e8..8362d54d 100644
--- a/gi_loadouts/data/weap/polearms/ptsg.py
+++ b/gi_loadouts/data/weap/polearms/ptsg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PrototypeStarglitter(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/rtrw.py b/gi_loadouts/data/weap/polearms/rtrw.py
index 79e2bf1e..9aaf8b5c 100644
--- a/gi_loadouts/data/weap/polearms/rtrw.py
+++ b/gi_loadouts/data/weap/polearms/rtrw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RightfulReward(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/rysp.py b/gi_loadouts/data/weap/polearms/rysp.py
index aa4e0a30..6c6979cd 100644
--- a/gi_loadouts/data/weap/polearms/rysp.py
+++ b/gi_loadouts/data/weap/polearms/rysp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RoyalSpear(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/sohm.py b/gi_loadouts/data/weap/polearms/sohm.py
index e4d05581..23ef06c9 100644
--- a/gi_loadouts/data/weap/polearms/sohm.py
+++ b/gi_loadouts/data/weap/polearms/sohm.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 """
 Dynamic calculation not implemented
diff --git a/gi_loadouts/data/weap/polearms/soss.py b/gi_loadouts/data/weap/polearms/soss.py
index df52e4c8..84454146 100644
--- a/gi_loadouts/data/weap/polearms/soss.py
+++ b/gi_loadouts/data/weap/polearms/soss.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 """
 Dynamic calculation not implemented
diff --git a/gi_loadouts/data/weap/polearms/swsp.py b/gi_loadouts/data/weap/polearms/swsp.py
index d16f291f..458708f6 100644
--- a/gi_loadouts/data/weap/polearms/swsp.py
+++ b/gi_loadouts/data/weap/polearms/swsp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SkywardSpine(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/tcth.py b/gi_loadouts/data/weap/polearms/tcth.py
index eba245fe..a51fc10b 100644
--- a/gi_loadouts/data/weap/polearms/tcth.py
+++ b/gi_loadouts/data/weap/polearms/tcth.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheCatch(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/vtvq.py b/gi_loadouts/data/weap/polearms/vtvq.py
index b06131fa..f9cecb17 100644
--- a/gi_loadouts/data/weap/polearms/vtvq.py
+++ b/gi_loadouts/data/weap/polearms/vtvq.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class VortexVanquisher(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/wbfn.py b/gi_loadouts/data/weap/polearms/wbfn.py
index e8fa846c..2ac43eaa 100644
--- a/gi_loadouts/data/weap/polearms/wbfn.py
+++ b/gi_loadouts/data/weap/polearms/wbfn.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WavebreakersFin(Polearm):
diff --git a/gi_loadouts/data/weap/polearms/wetl.py b/gi_loadouts/data/weap/polearms/wetl.py
index a8c89fa7..2a171520 100644
--- a/gi_loadouts/data/weap/polearms/wetl.py
+++ b/gi_loadouts/data/weap/polearms/wetl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Polearm, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WhiteTassel(Polearm):
diff --git a/gi_loadouts/data/weap/swords/__init__.py b/gi_loadouts/data/weap/swords/__init__.py
index 98a58541..e837b018 100644
--- a/gi_loadouts/data/weap/swords/__init__.py
+++ b/gi_loadouts/data/weap/swords/__init__.py
@@ -46,48 +46,48 @@
 )
 
 SwordsDict = {
-    absn.Absolution().name: absn.Absolution(),
-    ankg.AmenomaKageuchi().name: ankg.AmenomaKageuchi(),
-    aqfv.AquilaFavonia().name: aqfv.AquilaFavonia(),
-    bcls.BlackcliffLongsword().name: bcls.BlackcliffLongsword(),
-    cnsd.CinnabarSpindle().name: cnsd.CinnabarSpindle(),
-    clsl.CoolSteel().name: clsl.CoolSteel(),
-    disd.DarkIronSword().name: disd.DarkIronSword(),
-    dlbd.DullBlade().name: dlbd.DullBlade(),
-    fvsd.FavoniusSword().name: fvsd.FavoniusSword(),
-    ftds.FesteringDesire().name: ftds.FesteringDesire(),
-    flbd.FilletBlade().name: flbd.FilletBlade(),
-    fotd.FinaleoftheDeep().name: fotd.FinaleoftheDeep(),
-    fcfm.FleuveCendreFerryman().name: fcfm.FleuveCendreFerryman(),
-    foel.FluteofEzpitzal().name: foel.FluteofEzpitzal(),
-    fdsn.FreedomSworn().name: fdsn.FreedomSworn(),
-    hgft.HaranGeppakuFutsu().name: hgft.HaranGeppakuFutsu(),
-    hbod.HarbingerofDawn().name: hbod.HarbingerofDawn(),
-    insg.IronSting().name: insg.IronSting(),
-    ksin.KagotsurubeIsshin().name: ksin.KagotsurubeIsshin(),
-    kokn.KeyofKhajNisut().name: kokn.KeyofKhajNisut(),
-    lofi.LightofFoliarIncision().name: lofi.LightofFoliarIncision(),
-    lnrr.LionsRoar().name: lnrr.LionsRoar(),
-    msrf.MistsplitterReforged().name: msrf.MistsplitterReforged(),
-    pmjc.PrimordialJadeCutter().name: pmjc.PrimordialJadeCutter(),
-    ptrc.PrototypeRancour().name: ptrc.PrototypeRancour(),
-    ryls.RoyalLongsword().name: ryls.RoyalLongsword(),
-    sfsd.SacrificialSword().name: sfsd.SacrificialSword(),
-    spbd.SapwoodBlade().name: spbd.SapwoodBlade(),
-    slsd.SilverSword().name: slsd.SilverSword(),
-    sotw.SplendorofTranquilWaters().name: sotw.SplendorofTranquilWaters(),
-    smsp.SummitShaper().name: smsp.SummitShaper(),
-    sods.SwordOfDescension().name: sods.SwordOfDescension(),
-    sonk.SwordOfNarzissenkreuz().name: sonk.SwordOfNarzissenkreuz(),
-    srsd.SkyriderSword().name: srsd.SkyriderSword(),
-    swbd.SkywardBlade().name: swbd.SkywardBlade(),
-    tayf.TheAlleyFlash().name: tayf.TheAlleyFlash(),
-    tbsd.TheBlackSword().name: tbsd.TheBlackSword(),
-    tdha.TheDockhandsAssistant().name: tdha.TheDockhandsAssistant(),
-    tflt.TheFlute().name: tflt.TheFlute(),
-    tksg.ToukabouShigure().name: tksg.ToukabouShigure(),
-    tvhs.TravelersHandySword().name: tvhs.TravelersHandySword(),
-    urmg.UrakuMisugiri().name: urmg.UrakuMisugiri(),
-    wffg.WolfFang().name: wffg.WolfFang(),
-    xpml.XiphosMoonlight().name: xpml.XiphosMoonlight()
+    "Absolution": absn.Absolution,
+    "Amenoma Kageuchi": ankg.AmenomaKageuchi,
+    "Aquila Favonia": aqfv.AquilaFavonia,
+    "Blackcliff Longsword": bcls.BlackcliffLongsword,
+    "Cinnabar Spindle": cnsd.CinnabarSpindle,
+    "Cool Steel": clsl.CoolSteel,
+    "Dark Iron Sword": disd.DarkIronSword,
+    "Dull Blade": dlbd.DullBlade,
+    "Favonius Sword": fvsd.FavoniusSword,
+    "Festering Desire": ftds.FesteringDesire,
+    "Fillet Blade": flbd.FilletBlade,
+    "Finale of the Deep": fotd.FinaleoftheDeep,
+    "Fleuve Cendre Ferryman": fcfm.FleuveCendreFerryman,
+    "Flute of Ezpitzal": foel.FluteofEzpitzal,
+    "Freedom-Sworn": fdsn.FreedomSworn,
+    "Haran Geppaku Futsu": hgft.HaranGeppakuFutsu,
+    "Harbinger of Dawn": hbod.HarbingerofDawn,
+    "Iron Sting": insg.IronSting,
+    "Kagotsurube Isshin": ksin.KagotsurubeIsshin,
+    "Key of Khaj-Nisut": kokn.KeyofKhajNisut,
+    "Light of Foliar Incision": lofi.LightofFoliarIncision,
+    "Lion's Roar": lnrr.LionsRoar,
+    "Mistsplitter Reforged": msrf.MistsplitterReforged,
+    "Primordial Jade Cutter": pmjc.PrimordialJadeCutter,
+    "Prototype Rancour": ptrc.PrototypeRancour,
+    "Royal Longsword": ryls.RoyalLongsword,
+    "Sacrificial Sword": sfsd.SacrificialSword,
+    "Sapwood Blade": spbd.SapwoodBlade,
+    "Silver Sword": slsd.SilverSword,
+    "Splendor of Tranquil Waters": sotw.SplendorofTranquilWaters,
+    "Summit Shaper": smsp.SummitShaper,
+    "Sword of Descension": sods.SwordOfDescension,
+    "Sword of Narzissenkreuz": sonk.SwordOfNarzissenkreuz,
+    "Skyrider Sword": srsd.SkyriderSword,
+    "Skyward Blade": swbd.SkywardBlade,
+    "The Alley Flash": tayf.TheAlleyFlash,
+    "The Black Sword": tbsd.TheBlackSword,
+    "The Dockhand's Assistant": tdha.TheDockhandsAssistant,
+    "The Flute": tflt.TheFlute,
+    "Toukabou Shigure": tksg.ToukabouShigure,
+    "Traveler's Handy Sword": tvhs.TravelersHandySword,
+    "Uraku Misugiri": urmg.UrakuMisugiri,
+    "Wolf-Fang": wffg.WolfFang,
+    "Xiphos' Moonlight": xpml.XiphosMoonlight
 }
diff --git a/gi_loadouts/data/weap/swords/absn.py b/gi_loadouts/data/weap/swords/absn.py
index b2a38e5c..5577326a 100644
--- a/gi_loadouts/data/weap/swords/absn.py
+++ b/gi_loadouts/data/weap/swords/absn.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class Absolution(Sword):
diff --git a/gi_loadouts/data/weap/swords/ankg.py b/gi_loadouts/data/weap/swords/ankg.py
index f1e8f48f..6548ff5a 100644
--- a/gi_loadouts/data/weap/swords/ankg.py
+++ b/gi_loadouts/data/weap/swords/ankg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class AmenomaKageuchi(Sword):
diff --git a/gi_loadouts/data/weap/swords/aqfv.py b/gi_loadouts/data/weap/swords/aqfv.py
index 11ca8373..ceab8d80 100644
--- a/gi_loadouts/data/weap/swords/aqfv.py
+++ b/gi_loadouts/data/weap/swords/aqfv.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class AquilaFavonia(Sword):
diff --git a/gi_loadouts/data/weap/swords/bcls.py b/gi_loadouts/data/weap/swords/bcls.py
index fb80b9b1..e0a19adf 100644
--- a/gi_loadouts/data/weap/swords/bcls.py
+++ b/gi_loadouts/data/weap/swords/bcls.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class BlackcliffLongsword(Sword):
diff --git a/gi_loadouts/data/weap/swords/clsl.py b/gi_loadouts/data/weap/swords/clsl.py
index 161fa1a1..1aa3d6d2 100644
--- a/gi_loadouts/data/weap/swords/clsl.py
+++ b/gi_loadouts/data/weap/swords/clsl.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class CoolSteel(Sword):
diff --git a/gi_loadouts/data/weap/swords/cnsd.py b/gi_loadouts/data/weap/swords/cnsd.py
index 564c0122..fc05b574 100644
--- a/gi_loadouts/data/weap/swords/cnsd.py
+++ b/gi_loadouts/data/weap/swords/cnsd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class CinnabarSpindle(Sword):
diff --git a/gi_loadouts/data/weap/swords/disd.py b/gi_loadouts/data/weap/swords/disd.py
index f9878c44..d5703849 100644
--- a/gi_loadouts/data/weap/swords/disd.py
+++ b/gi_loadouts/data/weap/swords/disd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class DarkIronSword(Sword):
diff --git a/gi_loadouts/data/weap/swords/dlbd.py b/gi_loadouts/data/weap/swords/dlbd.py
index bf4bd6cc..8af0054f 100644
--- a/gi_loadouts/data/weap/swords/dlbd.py
+++ b/gi_loadouts/data/weap/swords/dlbd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class DullBlade(Sword):
diff --git a/gi_loadouts/data/weap/swords/fcfm.py b/gi_loadouts/data/weap/swords/fcfm.py
index 33d32b02..ef6e618c 100644
--- a/gi_loadouts/data/weap/swords/fcfm.py
+++ b/gi_loadouts/data/weap/swords/fcfm.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FleuveCendreFerryman(Sword):
diff --git a/gi_loadouts/data/weap/swords/fdsn.py b/gi_loadouts/data/weap/swords/fdsn.py
index 8808caf5..ea3e26d5 100644
--- a/gi_loadouts/data/weap/swords/fdsn.py
+++ b/gi_loadouts/data/weap/swords/fdsn.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FreedomSworn(Sword):
diff --git a/gi_loadouts/data/weap/swords/flbd.py b/gi_loadouts/data/weap/swords/flbd.py
index 8130e0e9..eb609bb0 100644
--- a/gi_loadouts/data/weap/swords/flbd.py
+++ b/gi_loadouts/data/weap/swords/flbd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FilletBlade(Sword):
diff --git a/gi_loadouts/data/weap/swords/foel.py b/gi_loadouts/data/weap/swords/foel.py
index e932c1d4..dc4c9d5e 100644
--- a/gi_loadouts/data/weap/swords/foel.py
+++ b/gi_loadouts/data/weap/swords/foel.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FluteofEzpitzal(Sword):
diff --git a/gi_loadouts/data/weap/swords/fotd.py b/gi_loadouts/data/weap/swords/fotd.py
index 2cd87f44..ffc744fb 100644
--- a/gi_loadouts/data/weap/swords/fotd.py
+++ b/gi_loadouts/data/weap/swords/fotd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FinaleoftheDeep(Sword):
diff --git a/gi_loadouts/data/weap/swords/ftds.py b/gi_loadouts/data/weap/swords/ftds.py
index 389b00f5..0b138759 100644
--- a/gi_loadouts/data/weap/swords/ftds.py
+++ b/gi_loadouts/data/weap/swords/ftds.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FesteringDesire(Sword):
diff --git a/gi_loadouts/data/weap/swords/fvsd.py b/gi_loadouts/data/weap/swords/fvsd.py
index 566ccf1a..8651e055 100644
--- a/gi_loadouts/data/weap/swords/fvsd.py
+++ b/gi_loadouts/data/weap/swords/fvsd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class FavoniusSword(Sword):
diff --git a/gi_loadouts/data/weap/swords/hbod.py b/gi_loadouts/data/weap/swords/hbod.py
index 54b45955..6e9b7b37 100644
--- a/gi_loadouts/data/weap/swords/hbod.py
+++ b/gi_loadouts/data/weap/swords/hbod.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class HarbingerofDawn(Sword):
diff --git a/gi_loadouts/data/weap/swords/hgft.py b/gi_loadouts/data/weap/swords/hgft.py
index 74337eb2..dd16a721 100644
--- a/gi_loadouts/data/weap/swords/hgft.py
+++ b/gi_loadouts/data/weap/swords/hgft.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class HaranGeppakuFutsu(Sword):
diff --git a/gi_loadouts/data/weap/swords/insg.py b/gi_loadouts/data/weap/swords/insg.py
index 6545a91f..a5571e35 100644
--- a/gi_loadouts/data/weap/swords/insg.py
+++ b/gi_loadouts/data/weap/swords/insg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class IronSting(Sword):
diff --git a/gi_loadouts/data/weap/swords/kokn.py b/gi_loadouts/data/weap/swords/kokn.py
index 26967146..875b6d3b 100644
--- a/gi_loadouts/data/weap/swords/kokn.py
+++ b/gi_loadouts/data/weap/swords/kokn.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class KeyofKhajNisut(Sword):
diff --git a/gi_loadouts/data/weap/swords/ksin.py b/gi_loadouts/data/weap/swords/ksin.py
index c9b2767f..8358f8fc 100644
--- a/gi_loadouts/data/weap/swords/ksin.py
+++ b/gi_loadouts/data/weap/swords/ksin.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class KagotsurubeIsshin(Sword):
diff --git a/gi_loadouts/data/weap/swords/lnrr.py b/gi_loadouts/data/weap/swords/lnrr.py
index c88fe433..b3521bde 100644
--- a/gi_loadouts/data/weap/swords/lnrr.py
+++ b/gi_loadouts/data/weap/swords/lnrr.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class LionsRoar(Sword):
diff --git a/gi_loadouts/data/weap/swords/lofi.py b/gi_loadouts/data/weap/swords/lofi.py
index 3d8849ba..63a7102c 100644
--- a/gi_loadouts/data/weap/swords/lofi.py
+++ b/gi_loadouts/data/weap/swords/lofi.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class LightofFoliarIncision(Sword):
diff --git a/gi_loadouts/data/weap/swords/msrf.py b/gi_loadouts/data/weap/swords/msrf.py
index eba4af5e..e3dbb3b0 100644
--- a/gi_loadouts/data/weap/swords/msrf.py
+++ b/gi_loadouts/data/weap/swords/msrf.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MistsplitterReforged(Sword):
diff --git a/gi_loadouts/data/weap/swords/pmjc.py b/gi_loadouts/data/weap/swords/pmjc.py
index 2459c563..e62b263b 100644
--- a/gi_loadouts/data/weap/swords/pmjc.py
+++ b/gi_loadouts/data/weap/swords/pmjc.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PrimordialJadeCutter(Sword):
diff --git a/gi_loadouts/data/weap/swords/ptrc.py b/gi_loadouts/data/weap/swords/ptrc.py
index d9da82c7..1faf8935 100644
--- a/gi_loadouts/data/weap/swords/ptrc.py
+++ b/gi_loadouts/data/weap/swords/ptrc.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class PrototypeRancour(Sword):
diff --git a/gi_loadouts/data/weap/swords/ryls.py b/gi_loadouts/data/weap/swords/ryls.py
index f45bd110..ac5745b4 100644
--- a/gi_loadouts/data/weap/swords/ryls.py
+++ b/gi_loadouts/data/weap/swords/ryls.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class RoyalLongsword(Sword):
diff --git a/gi_loadouts/data/weap/swords/sfsd.py b/gi_loadouts/data/weap/swords/sfsd.py
index a080969c..a010c431 100644
--- a/gi_loadouts/data/weap/swords/sfsd.py
+++ b/gi_loadouts/data/weap/swords/sfsd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SacrificialSword(Sword):
diff --git a/gi_loadouts/data/weap/swords/slsd.py b/gi_loadouts/data/weap/swords/slsd.py
index 05451665..882bd787 100644
--- a/gi_loadouts/data/weap/swords/slsd.py
+++ b/gi_loadouts/data/weap/swords/slsd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SilverSword(Sword):
diff --git a/gi_loadouts/data/weap/swords/smsp.py b/gi_loadouts/data/weap/swords/smsp.py
index e1d6f63f..bd8b69d2 100644
--- a/gi_loadouts/data/weap/swords/smsp.py
+++ b/gi_loadouts/data/weap/swords/smsp.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SummitShaper(Sword):
diff --git a/gi_loadouts/data/weap/swords/sods.py b/gi_loadouts/data/weap/swords/sods.py
index 0bd4848a..347ba4d3 100644
--- a/gi_loadouts/data/weap/swords/sods.py
+++ b/gi_loadouts/data/weap/swords/sods.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SwordOfDescension(Sword):
diff --git a/gi_loadouts/data/weap/swords/sonk.py b/gi_loadouts/data/weap/swords/sonk.py
index bebc917a..1495b46f 100644
--- a/gi_loadouts/data/weap/swords/sonk.py
+++ b/gi_loadouts/data/weap/swords/sonk.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SwordOfNarzissenkreuz(Sword):
diff --git a/gi_loadouts/data/weap/swords/sotw.py b/gi_loadouts/data/weap/swords/sotw.py
index cb328bbd..2b22f2e5 100644
--- a/gi_loadouts/data/weap/swords/sotw.py
+++ b/gi_loadouts/data/weap/swords/sotw.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SplendorofTranquilWaters(Sword):
diff --git a/gi_loadouts/data/weap/swords/spbd.py b/gi_loadouts/data/weap/swords/spbd.py
index 1738f55e..24d644af 100644
--- a/gi_loadouts/data/weap/swords/spbd.py
+++ b/gi_loadouts/data/weap/swords/spbd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SapwoodBlade(Sword):
diff --git a/gi_loadouts/data/weap/swords/srsd.py b/gi_loadouts/data/weap/swords/srsd.py
index fb3e7d1b..78f416da 100644
--- a/gi_loadouts/data/weap/swords/srsd.py
+++ b/gi_loadouts/data/weap/swords/srsd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SkyriderSword(Sword):
diff --git a/gi_loadouts/data/weap/swords/swbd.py b/gi_loadouts/data/weap/swords/swbd.py
index c103a431..6220ca9c 100644
--- a/gi_loadouts/data/weap/swords/swbd.py
+++ b/gi_loadouts/data/weap/swords/swbd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class SkywardBlade(Sword):
diff --git a/gi_loadouts/data/weap/swords/tayf.py b/gi_loadouts/data/weap/swords/tayf.py
index ed59636e..91d14bda 100644
--- a/gi_loadouts/data/weap/swords/tayf.py
+++ b/gi_loadouts/data/weap/swords/tayf.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheAlleyFlash(Sword):
diff --git a/gi_loadouts/data/weap/swords/tbsd.py b/gi_loadouts/data/weap/swords/tbsd.py
index 2637d16d..615f6895 100644
--- a/gi_loadouts/data/weap/swords/tbsd.py
+++ b/gi_loadouts/data/weap/swords/tbsd.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheBlackSword(Sword):
diff --git a/gi_loadouts/data/weap/swords/tdha.py b/gi_loadouts/data/weap/swords/tdha.py
index 8d0679a9..b2ec24ca 100644
--- a/gi_loadouts/data/weap/swords/tdha.py
+++ b/gi_loadouts/data/weap/swords/tdha.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheDockhandsAssistant(Sword):
diff --git a/gi_loadouts/data/weap/swords/tflt.py b/gi_loadouts/data/weap/swords/tflt.py
index 756a8a2d..1b75a0d4 100644
--- a/gi_loadouts/data/weap/swords/tflt.py
+++ b/gi_loadouts/data/weap/swords/tflt.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TheFlute(Sword):
diff --git a/gi_loadouts/data/weap/swords/tksg.py b/gi_loadouts/data/weap/swords/tksg.py
index 16b7a3ca..b0baeeed 100644
--- a/gi_loadouts/data/weap/swords/tksg.py
+++ b/gi_loadouts/data/weap/swords/tksg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class ToukabouShigure(Sword):
diff --git a/gi_loadouts/data/weap/swords/tvhs.py b/gi_loadouts/data/weap/swords/tvhs.py
index dfab5dbd..20cb8ba1 100644
--- a/gi_loadouts/data/weap/swords/tvhs.py
+++ b/gi_loadouts/data/weap/swords/tvhs.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class TravelersHandySword(Sword):
diff --git a/gi_loadouts/data/weap/swords/urmg.py b/gi_loadouts/data/weap/swords/urmg.py
index 239398c7..634d27d0 100644
--- a/gi_loadouts/data/weap/swords/urmg.py
+++ b/gi_loadouts/data/weap/swords/urmg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class UrakuMisugiri(Sword):
diff --git a/gi_loadouts/data/weap/swords/wffg.py b/gi_loadouts/data/weap/swords/wffg.py
index 3139211c..39be29d9 100644
--- a/gi_loadouts/data/weap/swords/wffg.py
+++ b/gi_loadouts/data/weap/swords/wffg.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WolfFang(Sword):
diff --git a/gi_loadouts/data/weap/swords/xpml.py b/gi_loadouts/data/weap/swords/xpml.py
index 99284b19..03a5852c 100644
--- a/gi_loadouts/data/weap/swords/xpml.py
+++ b/gi_loadouts/data/weap/swords/xpml.py
@@ -2,7 +2,7 @@
 
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.weap import Sword, WeaponStat, WeaponStatType
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class XiphosMoonlight(Sword):
diff --git a/gi_loadouts/face/rsrc/__init__.py b/gi_loadouts/face/rsrc/__init__.py
index f16be198..fc8cb0da 100644
--- a/gi_loadouts/face/rsrc/__init__.py
+++ b/gi_loadouts/face/rsrc/__init__.py
@@ -1,4 +1,3 @@
-import os
 from os import path, remove
 from pathlib import Path
 from re import match
@@ -23,10 +22,16 @@ def make_temp_file() -> None:
     Remove the residual cache data from the temporary directory left over during previous sessions
     due to unsuccessful termination before instantiating the same for this session.
     """
-    ptrn = r"gi-loadouts-[A-Za-z0-9]+\.traineddata"
+    ptrn = fr"{conf.data_prefix}[a-z0-9_]+\{conf.data_suffix}"
     temp = Path(gettempdir())
     resi = [temp / file.name for file in temp.iterdir() if file.is_file() if match(ptrn, file.name)]
-    _ = [os.remove(file) for file in resi if file.exists()]
+
+    for file in resi:
+        if file.exists():
+            try:
+                remove(file)
+            except FileNotFoundError:
+                continue
 
     file = QFile(":data/data/best.traineddata")
     if not file.open(QIODevice.ReadOnly):
@@ -40,10 +45,10 @@ def make_temp_file() -> None:
     have to be created and deleted manually. On UNIX based operating systems like GNU/Linux or
     MacOS, files can be reliably opened even when they have been marked for deletion.
     """
-    temp = NamedTemporaryFile(prefix="gi-loadouts-", suffix=".traineddata", delete=False, mode="w+b")
+    temp = NamedTemporaryFile(prefix=conf.data_prefix, suffix=conf.data_suffix, delete=False, mode="w+b")
     temp.write(cont)
     temp.close()
-    conf.tempname = Path(temp.name).name.replace(".traineddata", "")
+    conf.tempname = Path(temp.name).name.replace(conf.data_suffix, "")
     conf.temppath = temp.name
 
 
diff --git a/gi_loadouts/face/scan/rule.py b/gi_loadouts/face/scan/rule.py
index 60bfef2c..d77f9f83 100644
--- a/gi_loadouts/face/scan/rule.py
+++ b/gi_loadouts/face/scan/rule.py
@@ -237,8 +237,18 @@ def register_return_from_scanning(self, rslt: tuple) -> None:
 
         :return:
         """
+        area, main, seco, team, levl, rare, duration, expt = rslt
+
+        if expt:
+            self.show_dialog(
+                QMessageBox.Information,
+                "Faulty scanning",
+                f"Please consider checking your input after ensuring that the proper Tesseract OCR executable has been selected.\n\n{expt}"
+            )
+            return
+
         self.arti_shot.setPixmap(self.shot)
-        area, main, seco, team, levl, rare, duration = rslt
+
         if area in [self.arti_dist.itemText(indx) for indx in range(self.arti_dist.count())]:
             self.arti_dist.setCurrentText(area)
         if team in [self.arti_type.itemText(indx) for indx in range(self.arti_type.count())]:
diff --git a/gi_loadouts/face/scan/work.py b/gi_loadouts/face/scan/work.py
index cd9f3d86..0ae4285b 100644
--- a/gi_loadouts/face/scan/work.py
+++ b/gi_loadouts/face/scan/work.py
@@ -23,7 +23,7 @@ def __init__(self, snap: ImageFile):
         super().__init__()
         self.snap = snap
 
-    def scan_artifact(self) -> None:
+    def scan_artifact(self) -> None:  # pragma: no cover
         """
         Scan the screenshot for computing artifact information using Tesseract OCR
 
@@ -31,6 +31,11 @@ def scan_artifact(self) -> None:
 
         :return: Collection of artifact information and duration of computation
         """
+        """
+        coverage.py does not seem to correctly work with the QThread.
+        Even though this part is tested but the coverage remains unchanged. https://github.com/nedbat/coveragepy/issues/686
+        """
+
         strttime = time()
 
         area, main, seco, team, levl, rare = "", ATTR(), {"a": ATTR(), "b": ATTR(), "c": ATTR(), "d": ATTR()}, "", "Level 00", "Star 0"
@@ -45,9 +50,13 @@ def scan_artifact(self) -> None:
             text = image_to_string(self.snap, lang=conf.tempname, config=f"--tessdata-dir {location}")
         except (OSError, TesseractError) as expt:
             if isinstance(expt, OSError):
-                raise OSError("Selected executable of Tesseract OCR is unfunctional.") from expt
-            elif isinstance(expt, TesseractError):
-                raise ValueError("Processing failed as either Tesseract OCR executable ceased to function or training data was tampered with.") from expt
+                expt = "Selected executable of Tesseract OCR is unfunctional."
+            else:
+                expt = "Processing failed as either Tesseract OCR executable ceased to function or training data was tampered with."
+            stoptime = time()
+            rslt = area, main, seco, team, levl, rare, (stoptime - strttime), expt
+            self.result.emit(rslt)
+            return
 
         # DISTRIBUTION AREA
         areadict = {}
@@ -148,6 +157,6 @@ def scan_artifact(self) -> None:
 
         stoptime = time()
 
-        rslt = area, main, seco, team, levl, rare, (stoptime - strttime)
+        rslt = area, main, seco, team, levl, rare, (stoptime - strttime), None
 
         self.result.emit(rslt)
diff --git a/gi_loadouts/face/util.py b/gi_loadouts/face/util.py
index 62449139..cb93de51 100644
--- a/gi_loadouts/face/util.py
+++ b/gi_loadouts/face/util.py
@@ -1,6 +1,6 @@
 from io import BytesIO
 
-from PIL import Image, ImageEnhance, ImageFilter, UnidentifiedImageError
+from PIL import Image, ImageEnhance, ImageFilter
 from PySide6.QtCore import QResource
 from PySide6.QtGui import QImage, QPixmap
 
@@ -27,16 +27,13 @@ def modify_graphics_resource(path: str, radius: float = 2.5, shadow: float = 0.5
     :param path: Path to the imported image file in the resource
     :return: Pixmap created on transformation for user interface
     """
-    try:
-        resource = QResource(path)
-        data = resource.data()
-        iobt = BytesIO(data)
-        proc = Image.open(iobt).convert("RGBA").filter(ImageFilter.GaussianBlur(radius=radius))
-        proc = ImageEnhance.Brightness(proc).enhance(shadow)
-        qimg = QImage(proc.tobytes(), proc.width, proc.height, QImage.Format_RGBA8888)
-        rtrn = QPixmap.fromImage(qimg)
-    except UnidentifiedImageError:
-        rtrn = QPixmap(path)
+    resource = QResource(path)
+    data = resource.data()
+    iobt = BytesIO(data)
+    proc = Image.open(iobt).convert("RGBA").filter(ImageFilter.GaussianBlur(radius=radius))
+    proc = ImageEnhance.Brightness(proc).enhance(shadow)
+    qimg = QImage(proc.tobytes(), proc.width, proc.height, QImage.Format_RGBA8888)
+    rtrn = QPixmap.fromImage(qimg)
     return rtrn
 
 
diff --git a/gi_loadouts/face/wind/calc.py b/gi_loadouts/face/wind/calc.py
index e217ea6e..a382764b 100644
--- a/gi_loadouts/face/wind/calc.py
+++ b/gi_loadouts/face/wind/calc.py
@@ -9,7 +9,7 @@
 
 
 class Assess:
-    def __init__(self) -> None:
+    def __init__(self) -> None:  # pragma: no cover
         self.collection = Collection()
         self.c_team = None
         self.c_weap = None
@@ -24,7 +24,7 @@ def char_keep(self) -> None:
         """
         if self.head_char_name.currentText().strip() != "" and self.head_char_levl.currentText().strip() != "":
             # MAIN
-            char = __charmaps__[self.head_char_name.currentText()]
+            char = __charmaps__[self.head_char_name.currentText()]()
             char.levl = getattr(Level, self.head_char_levl.currentText().replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
 
             # BASE
@@ -60,7 +60,7 @@ def char_stat(self) -> None:
             DEF % scaling = Noelle,
             HP  % scaling = Baizhu, Barbara, Candace, Chevreuse, Dehya, Dori, Kirara, Kuki Shinobu, Mika, Nilou, Yaoyao
             """
-            char = __charmaps__[self.head_char_name.currentText()]
+            char = __charmaps__[self.head_char_name.currentText()]()
             char.levl = getattr(Level, self.head_char_levl.currentText().replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
             char_substats_prev_data = getattr(self.c_tyvt, self.c_tyvt.revmap[char.seco.stat_name]).stat_data
             char_substats_curt_data = getattr(self.c_char, self.c_char.revmap[char.seco.stat_name]).stat_data
@@ -107,7 +107,7 @@ def weap_keep(self) -> None:
             # MAIN
             kind = self.weap_area_type.currentText().strip()
             name = self.weap_area_name.currentText().strip()
-            weap = Family[kind][name]
+            weap = Family[kind][name]()
             refn = self.weap_area_refn.currentText().strip()
             weap.levl = getattr(Level, self.weap_area_levl.currentText().replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
             self.c_weap.base = ATTR(stat_name=WeaponStatType.attack.value, stat_data=weap.main_stat.stat_data)
diff --git a/gi_loadouts/face/wind/fclt.py b/gi_loadouts/face/wind/fclt.py
index b8c9a76e..3e5f7da9 100644
--- a/gi_loadouts/face/wind/fclt.py
+++ b/gi_loadouts/face/wind/fclt.py
@@ -26,7 +26,7 @@
 
 class Facility(Dialog):
     def __init__(self) -> None:
-        super().__init__()
+        super().__init__()  # pragma: no cover
 
     def arti_save(self, part: str) -> None:
         """
@@ -36,9 +36,6 @@ def arti_save(self, part: str) -> None:
         :return:
         """
         try:
-            if getattr(self, f"arti_{part}_type").currentText().strip() == "":
-                raise ValueError("Artifact type cannot be identified.")
-
             objc = ArtiFile(
                 type=getattr(ArtiList, getattr(self, f"arti_{part}_type").currentText().strip().replace(" ", "_").replace("'", "").replace("-", "_")),
                 levl=getattr(ArtiLevl, getattr(self, f"arti_{part}_levl").currentText().strip().replace(" ", "_")),
@@ -94,8 +91,6 @@ def team_save(self) -> None:
             objc = TeamFile()
             arealist = ["fwol", "pmod", "sdoe", "gboe", "ccol"]
             for part in arealist:
-                if getattr(self, f"arti_{part}_type").currentText().strip() == "":
-                    raise ValueError("Artifact type cannot be identified.")
                 unit = ArtiFile(
                     type=getattr(ArtiList, getattr(self, f"arti_{part}_type").currentText().strip().replace(" ", "_").replace("'", "").replace("-", "_")),
                     levl=getattr(ArtiLevl, getattr(self, f"arti_{part}_levl").currentText().strip().replace(" ", "_")),
@@ -186,9 +181,6 @@ def arti_load(self, part: str) -> None:
                 objc = json.loads(data)
                 arti = make_artifile_from_good(objc)
 
-            if arti.area.value != part.upper():
-                raise ValueError("Artifact area cannot be identified.")
-
             droptype = getattr(self, f"arti_{part}_type")
             droptype.setCurrentText(arti.type.value.name)
             droprare = getattr(self, f"arti_{part}_rare")
@@ -243,9 +235,6 @@ def team_load(self) -> None:
 
             for part in ["fwol", "pmod", "sdoe", "gboe", "ccol"]:
                 arti = getattr(team, part)
-                if arti.area.value != part.upper():
-                    raise ValueError("Artifact area cannot be identified.")
-
                 droptype = getattr(self, f"arti_{part}_type")
                 droptype.setCurrentText(arti.type.value.name)
                 droprare = getattr(self, f"arti_{part}_rare")
@@ -308,9 +297,6 @@ def weap_load(self) -> None:
                 raise ValueError("Weapon name cannot be identified.")
             self.weap_area_name.setCurrentText(weap.name)
 
-            levllist = [self.weap_area_levl.itemText(indx) for indx in range(self.weap_area_levl.count())]
-            if weap.levl.value.name not in levllist:
-                raise ValueError("Weapon level cannot be parsed.")
             self.weap_area_levl.setCurrentText(weap.levl.value.name)
 
             refnlist = [self.weap_area_refn.itemText(indx) for indx in range(self.weap_area_refn.count())]
diff --git a/gi_loadouts/face/wind/file.py b/gi_loadouts/face/wind/file.py
index f420b3e8..fd53cf20 100644
--- a/gi_loadouts/face/wind/file.py
+++ b/gi_loadouts/face/wind/file.py
@@ -4,13 +4,15 @@
 from PySide6.QtWidgets import QFileDialog
 
 from gi_loadouts.type.file.arti import ArtiFile
+from gi_loadouts.type.file.team import TeamFile
+from gi_loadouts.type.file.weap import WeapFile
 
 
 class FileHandling:
     def __init__(self):
         super().__init__()
 
-    def save(self, prnt, head: str, path: str, data: ArtiFile) -> bool:
+    def save(self, prnt, head: str, path: str, data: ArtiFile | TeamFile | WeapFile) -> bool:
         """
         Handle file operations involved in saving data to the storage device
 
diff --git a/gi_loadouts/face/wind/main.py b/gi_loadouts/face/wind/main.py
index bfe449bf..42339909 100644
--- a/gi_loadouts/face/wind/main.py
+++ b/gi_loadouts/face/wind/main.py
@@ -9,12 +9,19 @@
 class MainWindow(Rule):
     def __init__(self) -> None:
         super().__init__()
-        self.dialog = QMessageBox()
+        self.dialog = QMessageBox(parent=self)
         self.setupUi(self)
         self.setWindowTitle(f"Loadouts for Genshin Impact v{__versdata__}")
         self.initialize_events()
         self.initialize_elements()
-        make_temp_file()
+        try:
+            make_temp_file()
+        except Exception as expt:
+            self.show_dialog(
+                QMessageBox.Information,
+                "Initialization failed",
+                f"{expt}",
+            )
 
     def __del__(self) -> None:
         kill_temp_file()
@@ -82,7 +89,6 @@ def initialize_events(self) -> None:
                 drop, text = getattr(self, f"arti_{part}_name_{alfa}"), getattr(self, f"arti_{part}_data_{alfa}")
                 drop.currentTextChanged.connect(lambda _, a_drop=drop, a_text=text: self.render_lineedit_readonly_when_none(a_drop, a_text))
                 text.textChanged.connect(self.validate_lineedit_userdata)
-        for part in ["fwol", "pmod", "sdoe", "gboe", "ccol"]:
             getattr(self, f"arti_{part}_scan").clicked.connect(lambda _, a_part=part: self.show_scan_dialog(a_part))
             getattr(self, f"arti_{part}_load").clicked.connect(lambda _, a_part=part: self.arti_load(a_part))
             getattr(self, f"arti_{part}_save").clicked.connect(lambda _, a_part=part: self.arti_save(a_part))
diff --git a/gi_loadouts/face/wind/rule.py b/gi_loadouts/face/wind/rule.py
index 6e844990..6402ee62 100644
--- a/gi_loadouts/face/wind/rule.py
+++ b/gi_loadouts/face/wind/rule.py
@@ -74,7 +74,7 @@ def handle_elem_data(self, _: str = "") -> None:
         if self.head_char_elem.currentText().strip() != "":
             self.head_char_name.clear()
             if self.head_char_elem.currentText().strip() != "All":
-                self.head_char_name.addItems([item for item, data in __charmaps__.items() if data.vision.value.name == self.head_char_elem.currentText().strip()])
+                self.head_char_name.addItems([item for item, data in __charmaps__.items() if data().vision.value.name == self.head_char_elem.currentText().strip()])
             else:
                 self.head_char_name.addItems([item for item, data in __charmaps__.items()])
 
@@ -101,7 +101,7 @@ def handle_char_data(self, _: str = "") -> None:
         :return:
         """
         if self.head_char_name.currentText().strip() != "" and self.head_char_levl.currentText().strip() != "":
-            char = __charmaps__[self.head_char_name.currentText()]
+            char = __charmaps__[self.head_char_name.currentText()]()
             char.levl = getattr(Level, self.head_char_levl.currentText().replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
             self.head_vson.setPixmap(QPixmap(f":vson/imgs/vson/{char.vision.value.name.lower()}.png"))
             self.head_area_back.setPixmap(modify_graphics_resource(f":name/imgs/char/name/{self.head_char_name.currentText().replace(" ", "_").lower()}.png", 1.0, 0.75))
@@ -116,7 +116,7 @@ def handle_char_data(self, _: str = "") -> None:
             self.head_area_line_prim.setText(f"{char.name}")
             self.head_area_line_seco.setText(f"{char.cons_name}")
             self.head_area_line_tert.setText(f"{char.weapon.value}")
-            self.head_area_line_quat.setText(f"<i>{char.name} is a {char.weapon.value.lower()}-wielding {char.vision.value.name if char.vision != Vision.none else ""} character of {char.rare.value.qant}-star quality.</i>")
+            self.head_area_line_quat.setText(f"<i>{char.name} is a {char.weapon.value.lower()}-wielding{f" {char.vision.value.name} " if char.vision != Vision.none else " "}character of {char.rare.value.qant}-star quality.</i>")
             self.head_area_line_quin.setText(f"<i>{char.name} is affiliated with {char.afln}.</i>" if char.afln != "" else f"<i>{char.name} is not affiliated with any association.</i>")
             self.manage_changing_appearance(char.vision.value.colour)
 
@@ -128,7 +128,7 @@ def format_weapon_by_char_change(self, _: str = "") -> None:
         :return:
         """
         if self.head_char_name.currentText().strip() != "" and self.head_char_levl.currentText().strip() != "":
-            char = __charmaps__[self.head_char_name.currentText()]
+            char = __charmaps__[self.head_char_name.currentText()]()
             char.levl = getattr(Level, self.head_char_levl.currentText().replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
             self.weap_area_type.clear()
             self.weap_area_type.addItem(f"{char.weapon.value}")
@@ -158,8 +158,8 @@ def convey_weapon_name_change(self, _: str = "") -> None:
             self.weap_area_refn.clear()
             self.weap_area_refn_head.setText("No refinements available.")
             self.weap_area_refn_body.setText("No refinements available.")
-            self.weap_area_stat.setText("No substats.")
-            weap = Family[kind][name]
+            self.weap_area_stat.setText("No substats")
+            weap = Family[kind][name]()
             self.weap_area_levl.addItems([item.value.name for item in weap.levl_bind])
             self.weap_area_refn.addItems(item for item in weap.refinement.keys())
             self.weap_port_area.setPixmap(QPixmap(weap.rare.value.back))
@@ -175,7 +175,7 @@ def convey_weapon_levl_change(self, _: str = "") -> None:
         if self.weap_area_type.currentText().strip() != "" and self.weap_area_name.currentText().strip() != "" and self.weap_area_levl.currentText().strip() != "":
             name = self.weap_area_name.currentText()
             kind = self.weap_area_type.currentText().strip()
-            weap = Family[kind][name]
+            weap = Family[kind][name]()
             weap.levl = getattr(Level, self.weap_area_levl.currentText().replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
             self.weap_area_batk.setText(f"ATK {round(weap.main_stat.stat_data)}")
             if weap.levl.value.rank.value <= 1:
@@ -195,7 +195,7 @@ def convey_refinement_change(self, _: str = "") -> None:
         if self.weap_area_type.currentText().strip() != "" and self.weap_area_name.currentText().strip() != "" and self.weap_area_refn.currentText().strip() != "":
             name = self.weap_area_name.currentText()
             kind = self.weap_area_type.currentText().strip()
-            weap = Family[kind][name]
+            weap = Family[kind][name]()
             self.weap_area_refn_head.setText(f"<b>{weap.refi_name}</b>")
             self.weap_area_refn_body.setText(f"{weap.refinement[self.weap_area_refn.currentText().strip()].data}")
 
@@ -241,7 +241,7 @@ def change_data_by_changing_level_or_stat(self, droplevl: QComboBox, droptype: Q
                 rare = getattr(Rare, droprare.currentText().replace(" ", "_"))
                 stat = getattr(arti, f"revmap_{part}")[dropstat.currentText().strip()]
                 item = getattr(team.value, part)
-                item.levl, item.rare, item.stat_name = levl.value.levl, rare.value.qant, stat
+                item.rare, item.levl, item.stat_name = rare.value.qant, levl.value.levl, stat
                 statdata.setText(f"{round(item.stat_data, 1)}")
 
     def change_artifact_team_by_changing_type(self, droptype: QComboBox, part: str) -> None:
@@ -374,7 +374,7 @@ def show_output_window(self):
             self.calc_stat()
             self.otptobjc = OtptWindow(
                 {
-                    "char": __charmaps__[self.head_char_name.currentText()],
+                    "char": __charmaps__[self.head_char_name.currentText()](),
                     "levl": self.head_char_levl.currentText().strip(),
                     "cons": self.head_char_cons.currentText().strip(),
                 },
@@ -450,7 +450,7 @@ def show_info_dialog(self) -> None:
         :return:
         """
         self.infoobjc = InfoDialog()
-        self.infoobjc.exec()
+        self.infoobjc.show()
 
     def show_lcns_dialog(self) -> None:
         """
@@ -459,7 +459,7 @@ def show_lcns_dialog(self) -> None:
         :return:
         """
         self.lcnsobjc = LcnsDialog()
-        self.lcnsobjc.exec()
+        self.lcnsobjc.show()
 
     def show_scan_dialog(self, part: str) -> None:
         """
diff --git a/gi_loadouts/face/wind/talk.py b/gi_loadouts/face/wind/talk.py
index 2cb121b1..e46b6742 100644
--- a/gi_loadouts/face/wind/talk.py
+++ b/gi_loadouts/face/wind/talk.py
@@ -2,7 +2,7 @@
 
 
 class Dialog:
-    def __init__(self) -> None:
+    def __init__(self) -> None:  # pragma: no cover
         self.dialog = QMessageBox(parent=self)
 
     def show_dialog(self, icon: QMessageBox.Icon, head: str, text: str) -> None:
diff --git a/gi_loadouts/face/wind/wind.py b/gi_loadouts/face/wind/wind.py
index 8071dae8..491cf03d 100644
--- a/gi_loadouts/face/wind/wind.py
+++ b/gi_loadouts/face/wind/wind.py
@@ -2025,7 +2025,7 @@ def retranslateUi(self, mainwind):
 #endif // QT_CONFIG(tooltip)
         self.arti_ccol_wipe.setText(QCoreApplication.translate("mainwind", "trash", None))
 #if QT_CONFIG(tooltip)
-        self.arti_ccol_scan.setToolTip(QCoreApplication.translate("mainwind", "Load", None))
+        self.arti_ccol_scan.setToolTip(QCoreApplication.translate("mainwind", "Scan", None))
 #endif // QT_CONFIG(tooltip)
         self.arti_ccol_scan.setText(QCoreApplication.translate("mainwind", "eye", None))
         self.arti_ccol_data_d.setPlaceholderText(QCoreApplication.translate("mainwind", "Data", None))
@@ -2057,7 +2057,7 @@ def retranslateUi(self, mainwind):
 #endif // QT_CONFIG(tooltip)
         self.arti_gboe_wipe.setText(QCoreApplication.translate("mainwind", "trash", None))
 #if QT_CONFIG(tooltip)
-        self.arti_gboe_scan.setToolTip(QCoreApplication.translate("mainwind", "Load", None))
+        self.arti_gboe_scan.setToolTip(QCoreApplication.translate("mainwind", "Scan", None))
 #endif // QT_CONFIG(tooltip)
         self.arti_gboe_scan.setText(QCoreApplication.translate("mainwind", "eye", None))
 #if QT_CONFIG(tooltip)
@@ -2077,7 +2077,7 @@ def retranslateUi(self, mainwind):
 #endif // QT_CONFIG(tooltip)
         self.arti_sdoe_wipe.setText(QCoreApplication.translate("mainwind", "trash", None))
 #if QT_CONFIG(tooltip)
-        self.arti_sdoe_scan.setToolTip(QCoreApplication.translate("mainwind", "Load", None))
+        self.arti_sdoe_scan.setToolTip(QCoreApplication.translate("mainwind", "Scan", None))
 #endif // QT_CONFIG(tooltip)
         self.arti_sdoe_scan.setText(QCoreApplication.translate("mainwind", "eye", None))
 #if QT_CONFIG(tooltip)
@@ -2097,7 +2097,7 @@ def retranslateUi(self, mainwind):
 #endif // QT_CONFIG(tooltip)
         self.arti_pmod_wipe.setText(QCoreApplication.translate("mainwind", "trash", None))
 #if QT_CONFIG(tooltip)
-        self.arti_pmod_scan.setToolTip(QCoreApplication.translate("mainwind", "Load", None))
+        self.arti_pmod_scan.setToolTip(QCoreApplication.translate("mainwind", "Scan", None))
 #endif // QT_CONFIG(tooltip)
         self.arti_pmod_scan.setText(QCoreApplication.translate("mainwind", "eye", None))
 #if QT_CONFIG(tooltip)
@@ -2117,7 +2117,7 @@ def retranslateUi(self, mainwind):
 #endif // QT_CONFIG(tooltip)
         self.arti_fwol_wipe.setText(QCoreApplication.translate("mainwind", "trash", None))
 #if QT_CONFIG(tooltip)
-        self.arti_fwol_scan.setToolTip(QCoreApplication.translate("mainwind", "Load", None))
+        self.arti_fwol_scan.setToolTip(QCoreApplication.translate("mainwind", "Scan", None))
 #endif // QT_CONFIG(tooltip)
         self.arti_fwol_scan.setText(QCoreApplication.translate("mainwind", "eye", None))
         self.pair_area_desc.setDocumentTitle(QCoreApplication.translate("mainwind", "pair_area_desc", None))
diff --git a/gi_loadouts/main.py b/gi_loadouts/main.py
index 26edd822..52a09773 100644
--- a/gi_loadouts/main.py
+++ b/gi_loadouts/main.py
@@ -25,7 +25,7 @@ def load_custom_font() -> None:
         QFontDatabase.addApplicationFont(indx)
 
 
-def main() -> None:
+def main() -> None:  # pragma: no cover
     """
     Initialize the user interface before starting up
 
@@ -40,5 +40,5 @@ def main() -> None:
     sys.exit(app.exec())
 
 
-if __name__ == "__main__":
+if __name__ == "__main__":  # pragma: no cover
     main()
diff --git a/gi_loadouts/type/arti/base/__init__.py b/gi_loadouts/type/arti/base.py
similarity index 100%
rename from gi_loadouts/type/arti/base/__init__.py
rename to gi_loadouts/type/arti/base.py
diff --git a/gi_loadouts/type/arti/ccol.py b/gi_loadouts/type/arti/ccol.py
index 2edeab24..8e196dd3 100644
--- a/gi_loadouts/type/arti/ccol.py
+++ b/gi_loadouts/type/arti/ccol.py
@@ -1,7 +1,7 @@
 from typing import Optional
 
 from gi_loadouts.type.arti.base import Artifact, MainStatType_CCOL, SecoStat
-from gi_loadouts.type.arti.base.stat import (
+from gi_loadouts.type.arti.stat import (
     attack_perc,
     critical_damage_perc,
     critical_rate_perc,
diff --git a/gi_loadouts/type/arti/fwol.py b/gi_loadouts/type/arti/fwol.py
index 066a193a..a98fae14 100644
--- a/gi_loadouts/type/arti/fwol.py
+++ b/gi_loadouts/type/arti/fwol.py
@@ -1,7 +1,7 @@
 from typing import Optional
 
 from gi_loadouts.type.arti.base import Artifact, MainStatType_FWOL, SecoStat
-from gi_loadouts.type.arti.base.stat import health_points
+from gi_loadouts.type.arti.stat import health_points
 
 
 class FWOL(Artifact):
diff --git a/gi_loadouts/type/arti/gboe.py b/gi_loadouts/type/arti/gboe.py
index f4cd6a51..4d382659 100644
--- a/gi_loadouts/type/arti/gboe.py
+++ b/gi_loadouts/type/arti/gboe.py
@@ -1,7 +1,7 @@
 from typing import Optional
 
 from gi_loadouts.type.arti.base import Artifact, MainStatType_GBOE, SecoStat
-from gi_loadouts.type.arti.base.stat import (
+from gi_loadouts.type.arti.stat import (
     attack_perc,
     damage_bonus_elemental_perc,
     damage_bonus_physical_perc,
diff --git a/gi_loadouts/type/arti/pmod.py b/gi_loadouts/type/arti/pmod.py
index bd2c4f4e..eb7c9130 100644
--- a/gi_loadouts/type/arti/pmod.py
+++ b/gi_loadouts/type/arti/pmod.py
@@ -1,7 +1,7 @@
 from typing import Optional
 
 from gi_loadouts.type.arti.base import Artifact, MainStatType_PMOD, SecoStat
-from gi_loadouts.type.arti.base.stat import attack
+from gi_loadouts.type.arti.stat import attack
 
 
 class PMOD(Artifact):
diff --git a/gi_loadouts/type/arti/sdoe.py b/gi_loadouts/type/arti/sdoe.py
index df63c4ee..4ba068f9 100644
--- a/gi_loadouts/type/arti/sdoe.py
+++ b/gi_loadouts/type/arti/sdoe.py
@@ -1,7 +1,7 @@
 from typing import Optional
 
 from gi_loadouts.type.arti.base import Artifact, MainStatType_SDOE, SecoStat
-from gi_loadouts.type.arti.base.stat import (
+from gi_loadouts.type.arti.stat import (
     attack_perc,
     defense_perc,
     elemental_mastery,
diff --git a/gi_loadouts/type/arti/base/stat.py b/gi_loadouts/type/arti/stat.py
similarity index 100%
rename from gi_loadouts/type/arti/base/stat.py
rename to gi_loadouts/type/arti/stat.py
diff --git a/gi_loadouts/type/char/__init__.py b/gi_loadouts/type/char/__init__.py
index fdfd4b7f..e7917618 100644
--- a/gi_loadouts/type/char/__init__.py
+++ b/gi_loadouts/type/char/__init__.py
@@ -181,8 +181,6 @@ def seco(self) -> SecoStat:
 class BaseStat(BaseModel):
     """
     Base stats of a character
-
-    SUSPECTED TO BE RESIDUAL CODE
     """
     attack: float = 0.0
     defense: float = 0.0
diff --git a/gi_loadouts/type/file/weap.py b/gi_loadouts/type/file/weap.py
index f966e1ff..a092ffa9 100644
--- a/gi_loadouts/type/file/weap.py
+++ b/gi_loadouts/type/file/weap.py
@@ -45,7 +45,7 @@ def to_good(self) -> dict:
             "key": __weaplist_good__[self.name],
             "level": self.levl.value.qant,
             "ascension": self.levl.value.rank.value,
-            "refinement": int(self.refn.split(" ")[1]),
+            "refinement": int(self.refn.split(" ")[1]) if self.refn != "" else self.refn,
             "type": self.type.name
         }
         return data
@@ -82,7 +82,7 @@ def make_weapfile_from_good(objc: dict) -> WeapFile:
             name=__weaplist_good_revmap__[objc["key"]],
             type=getattr(WeaponType, objc["type"]),
             levl=getattr(Level, f"Level_0{objc["level"]}_{__ascn_bond__[objc["ascension"]]}_Rank_{objc["ascension"]}" if objc["level"] < 10 else f"Level_{objc["level"]}_{__ascn_bond__[objc["ascension"]]}_Rank_{objc["ascension"]}"),
-            refn=f"Refinement {objc["refinement"]}",
+            refn=f"Refinement {objc["refinement"]}" if objc["refinement"] != "" else objc["refinement"],
         )
     except Exception as expt:
         raise ValueError("Weapon data cannot be parsed.") from expt
diff --git a/gi_loadouts/type/weap/__init__.py b/gi_loadouts/type/weap/__init__.py
index 2ad0b2cd..da149371 100644
--- a/gi_loadouts/type/weap/__init__.py
+++ b/gi_loadouts/type/weap/__init__.py
@@ -6,9 +6,9 @@
 from gi_loadouts.type.levl import Level
 from gi_loadouts.type.rare import Rare
 from gi_loadouts.type.stat import STAT
-from gi_loadouts.type.weap.base.ascn import Ascn
-from gi_loadouts.type.weap.base.mult import Mult, MultSeco
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.ascn import Ascn
+from gi_loadouts.type.weap.mult import Mult, MultSeco
+from gi_loadouts.type.weap.tier import Tier
 
 
 class WeaponStatType(Enum):
diff --git a/gi_loadouts/type/weap/base/ascn.py b/gi_loadouts/type/weap/ascn.py
similarity index 100%
rename from gi_loadouts/type/weap/base/ascn.py
rename to gi_loadouts/type/weap/ascn.py
diff --git a/gi_loadouts/type/weap/base/seco/hpak.py b/gi_loadouts/type/weap/base/seco/hpak.py
deleted file mode 100644
index fec17daa..00000000
--- a/gi_loadouts/type/weap/base/seco/hpak.py
+++ /dev/null
@@ -1,158 +0,0 @@
-from gi_loadouts.type.rank import Rank
-from gi_loadouts.type.rare import Rare
-from gi_loadouts.type.weap.base.tier import Tier
-
-HPAK = {
-    Rank.Rank_0: {
-        Rare.Star_3: {
-            Tier.Tier_1: 10.21,
-            Tier.Tier_2: 7.66,
-            Tier.Tier_3: 5.11,
-        },
-        Rare.Star_4: {
-            Tier.Tier_1: 12.00,
-            Tier.Tier_2: 9.00,
-            Tier.Tier_3: 6.00,
-            Tier.Tier_4: 3.00,
-        },
-        Rare.Star_5: {
-            Tier.Tier_1: 14.40,
-            Tier.Tier_2: 10.80,
-            Tier.Tier_3: 7.20,
-            Tier.Tier_4: 3.60,
-        },
-    },
-    Rank.Rank_1: {
-        Rare.Star_3: {
-            Tier.Tier_1: 18.05,
-            Tier.Tier_2: 13.54,
-            Tier.Tier_3: 9.02,
-        },
-        Rare.Star_4: {
-            Tier.Tier_1: 21.20,
-            Tier.Tier_2: 15.90,
-            Tier.Tier_3: 10.60,
-            Tier.Tier_4: 5.30,
-        },
-        Rare.Star_5: {
-            Tier.Tier_1: 25.45,
-            Tier.Tier_2: 19.08,
-            Tier.Tier_3: 12.72,
-            Tier.Tier_4: 6.36,
-        },
-    },
-    Rank.Rank_2: {
-        Rare.Star_3: {
-            Tier.Tier_1: 26.30,
-            Tier.Tier_2: 19.73,
-            Tier.Tier_3: 13.15,
-        },
-        Rare.Star_4: {
-            Tier.Tier_1: 30.90,
-            Tier.Tier_2: 23.18,
-            Tier.Tier_3: 15.45,
-            Tier.Tier_4: 7.73,
-        },
-        Rare.Star_5: {
-            Tier.Tier_1: 37.08,
-            Tier.Tier_2: 27.81,
-            Tier.Tier_3: 18.54,
-            Tier.Tier_4: 9.27,
-        },
-    },
-    Rank.Rank_3: {
-        Rare.Star_3: {
-            Tier.Tier_1: 30.43,
-            Tier.Tier_2: 22.82,
-            Tier.Tier_3: 15.21,
-        },
-        Rare.Star_4: {
-            Tier.Tier_1: 35.75,
-            Tier.Tier_2: 26.81,
-            Tier.Tier_3: 17.87,
-            Tier.Tier_4: 8.94,
-        },
-        Rare.Star_5: {
-            Tier.Tier_1: 42.90,
-            Tier.Tier_2: 32.17,
-            Tier.Tier_3: 21.45,
-            Tier.Tier_4: 10.72,
-        },
-    },
-    Rank.Rank_4: {
-        Rare.Star_3: {
-            Tier.Tier_1: 34.55,
-            Tier.Tier_2: 25.91,
-            Tier.Tier_3: 17.28,
-        },
-        Rare.Star_4: {
-            Tier.Tier_1: 40.60,
-            Tier.Tier_2: 30.45,
-            Tier.Tier_3: 20.30,
-            Tier.Tier_4: 10.15,
-        },
-        Rare.Star_5: {
-            Tier.Tier_1: 48.72,
-            Tier.Tier_2: 36.54,
-            Tier.Tier_3: 24.36,
-            Tier.Tier_4: 12.18,
-        },
-    },
-    Rank.Rank_5: {
-        Rare.Star_3: {
-            Tier.Tier_1: 38.67,
-            Tier.Tier_2: 29.00,
-            Tier.Tier_3: 19.33,
-        },
-        Rare.Star_4: {
-            Tier.Tier_1: 45.43,
-            Tier.Tier_2: 34.07,
-            Tier.Tier_3: 22.72,
-            Tier.Tier_4: 11.36,
-        },
-        Rare.Star_5: {
-            Tier.Tier_1: 54.52,
-            Tier.Tier_2: 40.89,
-            Tier.Tier_3: 27.26,
-            Tier.Tier_4: 13.63,
-        },
-    },
-    Rank.Rank_6: {
-        Rare.Star_3: {
-            Tier.Tier_1: 42.79,
-            Tier.Tier_2: 32.10,
-            Tier.Tier_3: 21.40,
-        },
-        Rare.Star_4: {
-            Tier.Tier_1: 50.28,
-            Tier.Tier_2: 37.71,
-            Tier.Tier_3: 25.14,
-            Tier.Tier_4: 12.57,
-        },
-        Rare.Star_5: {
-            Tier.Tier_1: 60.34,
-            Tier.Tier_2: 45.25,
-            Tier.Tier_3: 30.17,
-            Tier.Tier_4: 15.08,
-        },
-    },
-    Rank.Rank_7: {
-        Rare.Star_3: {
-            Tier.Tier_1: 46.92,
-            Tier.Tier_2: 35.19,
-            Tier.Tier_3: 23.46,
-        },
-        Rare.Star_4: {
-            Tier.Tier_1: 55.13,
-            Tier.Tier_2: 41.35,
-            Tier.Tier_3: 27.56,
-            Tier.Tier_4: 13.78,
-        },
-        Rare.Star_5: {
-            Tier.Tier_1: 66.15,
-            Tier.Tier_2: 49.62,
-            Tier.Tier_3: 33.08,
-            Tier.Tier_4: 16.54,
-        },
-    }
-}
diff --git a/gi_loadouts/type/weap/base/mult.py b/gi_loadouts/type/weap/mult.py
similarity index 99%
rename from gi_loadouts/type/weap/base/mult.py
rename to gi_loadouts/type/weap/mult.py
index d1266dac..653e7edc 100644
--- a/gi_loadouts/type/weap/base/mult.py
+++ b/gi_loadouts/type/weap/mult.py
@@ -1,7 +1,7 @@
 from pydantic import BaseModel
 
 from gi_loadouts.type.rare import Rare
-from gi_loadouts.type.weap.base.tier import Tier
+from gi_loadouts.type.weap.tier import Tier
 
 
 class MultTier(BaseModel):
diff --git a/gi_loadouts/type/weap/base/tier.py b/gi_loadouts/type/weap/tier.py
similarity index 100%
rename from gi_loadouts/type/weap/base/tier.py
rename to gi_loadouts/type/weap/tier.py
diff --git a/poetry.lock b/poetry.lock
index a9d8144b..20906b38 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -115,9 +115,6 @@ files = [
     {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"},
 ]
 
-[package.dependencies]
-tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
-
 [package.extras]
 toml = ["tomli"]
 
@@ -133,18 +130,18 @@ files = [
 ]
 
 [[package]]
-name = "exceptiongroup"
-version = "1.2.2"
-description = "Backport of PEP 654 (exception groups)"
+name = "execnet"
+version = "2.1.1"
+description = "execnet: rapid multi-Python deployment"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
-    {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
+    {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
+    {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
 ]
 
 [package.extras]
-test = ["pytest (>=6)"]
+testing = ["hatch", "pre-commit", "pytest", "tox"]
 
 [[package]]
 name = "filelock"
@@ -173,6 +170,232 @@ files = [
     {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
 ]
 
+[[package]]
+name = "jinja2"
+version = "3.1.4"
+description = "A very fast and expressive template engine."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
+    {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "linkify-it-py"
+version = "2.0.3"
+description = "Links recognition library with FULL unicode support."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"},
+    {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"},
+]
+
+[package.dependencies]
+uc-micro-py = "*"
+
+[package.extras]
+benchmark = ["pytest", "pytest-benchmark"]
+dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"]
+doc = ["myst-parser", "sphinx", "sphinx-book-theme"]
+test = ["coverage", "pytest", "pytest-cov"]
+
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+description = "Python port of markdown-it. Markdown parsing, done right!"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
+    {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
+]
+
+[package.dependencies]
+linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""}
+mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""}
+mdurl = ">=0.1,<1.0"
+
+[package.extras]
+benchmarking = ["psutil", "pytest", "pytest-benchmark"]
+code-style = ["pre-commit (>=3.0,<4.0)"]
+compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
+linkify = ["linkify-it-py (>=1,<3)"]
+plugins = ["mdit-py-plugins"]
+profiling = ["gprof2dot"]
+rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
+[[package]]
+name = "markupsafe"
+version = "2.1.5"
+description = "Safely add untrusted strings to HTML/XML markup."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
+    {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
+    {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
+    {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
+    {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
+    {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
+    {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
+    {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
+]
+
+[[package]]
+name = "mdit-py-plugins"
+version = "0.4.2"
+description = "Collection of plugins for markdown-it-py"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"},
+    {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=1.0.0,<4.0.0"
+
+[package.extras]
+code-style = ["pre-commit"]
+rtd = ["myst-parser", "sphinx-book-theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+description = "Markdown URL utilities"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
+    {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
+]
+
+[[package]]
+name = "memray"
+version = "1.14.0"
+description = "A memory profiler for Python applications"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+    {file = "memray-1.14.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:745d9014cb662065501441a7b534c29914fe2b68398b37385aba9f4a1c51c723"},
+    {file = "memray-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f62a402ca1a7126f749544c3d6493672d6330ffd37d59ba230bc73e5143b3bc2"},
+    {file = "memray-1.14.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:36840f39277b1871ecb5a9592dd1aa517a17b9f855f4e3ff08aa328a9d305e69"},
+    {file = "memray-1.14.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c7933ca70c0d59d0ce9b1064a6eda86231248759b46ed6dabedf489039d1aa1"},
+    {file = "memray-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a5907345ff845652e709ddce3171a9ba2d65c62e8bd49a99131066e2a7ce3b"},
+    {file = "memray-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:88c89c3797834eec177a89ad528699c75b94e2ed08c00754141eae69c520b894"},
+    {file = "memray-1.14.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:d6087f291fd68acdf0a833efb57bc0f192c98ae89b4377c690c28313e78d029c"},
+    {file = "memray-1.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e6ba7bff9dfa37bf3b80a5b83b50eadf20afb1f0e8de4a0139019154086d6bed"},
+    {file = "memray-1.14.0-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9bb0cfe1b755a860435cd52047b2e3f4f7b0c3887e0c1bf98da7127948284a91"},
+    {file = "memray-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:638ba74e1735a40b6595fee7f37b426b9a95d244091a1f5df3dc5d98df1cbd4b"},
+    {file = "memray-1.14.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7227ebf024cb0688a68ed91ed3e05c61a13751a9e875807195076b827bfde464"},
+    {file = "memray-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:248dea8cfb5a615345e28b7e25c94377a8d198da3b6957ee443afa6f4ff1b733"},
+    {file = "memray-1.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d03f6be66aa259df7fa50082876fbe6461108d77d46c1f720c46067d60685d4"},
+    {file = "memray-1.14.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:9af9d30b1e484fd8591c9a7f322fd50b9192a2bce660be92385a01555af9968b"},
+    {file = "memray-1.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c4088b391c04796c888ac751b5d387f6e8212b3515d4c53ba540c65a6efe4bda"},
+    {file = "memray-1.14.0-cp312-cp312-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:af8aee7e8e5cac1e4130f1184b3e03b6bb08264e4ba1696551791ed3f8fb824e"},
+    {file = "memray-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4352f9e85957f2cbe45a9e1c87dfc19d2df77e93dcd8a558794a683eeee57b7b"},
+    {file = "memray-1.14.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5953f0d2aa31b23d4cce20236a03d90b7c71903709a57b456d6494bfe6f470b7"},
+    {file = "memray-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4ccaca04365efcda51036fe2add980030e33cfc4f3a194a01f530a5c923c65"},
+    {file = "memray-1.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f85a27eb8a65c810161bb992116a66d328546f78a4a4c7c1868949651b917c08"},
+    {file = "memray-1.14.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:958d57f7149b8fa4831785394f2a7ace93dbc2be6c49a1c07987a8972986474a"},
+    {file = "memray-1.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287a01953bc44dd0a32549a23bdacb5f9734e345ca289fa3923867c637715056"},
+    {file = "memray-1.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc17cba35d98e3d2ca20ab995f17eec3edba7138b062cbc1aa36d53d9d2d955"},
+    {file = "memray-1.14.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c82342cead930ca50235f59740ca238808f9c33ef31d994712972966beb6226e"},
+    {file = "memray-1.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22a826b4047e839310514f4889c24e45a66ea222fca19ac0ae7b2f89bbb0281"},
+    {file = "memray-1.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:344f3c73b97ffc8f1666b404deafbc31a19e6b2881341b706aa7ec20afb0e8b1"},
+    {file = "memray-1.14.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a43455233d534e9c0e8dabe827d451124874a6455b2afcbcd60b823241ea5843"},
+    {file = "memray-1.14.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e05a3b6bc82ef01821beaee98e86bd8de2ada06cb8781add9c40a3ae4a040383"},
+    {file = "memray-1.14.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bc27e5483c70236c9379b99277b4ea8fa4b3f73a99e37af81190649bd877881"},
+    {file = "memray-1.14.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a7e5604448b2a78e329addfb099384515d3f973a03711c4e2a7b6c9f7f34f53"},
+    {file = "memray-1.14.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:443885a96ab9f67d46288240e2593b5c3ecb2c507ddb4e3b10695e104403d001"},
+    {file = "memray-1.14.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:52a45d96ed717d8efb645e99646a92dd21a2ca38bdb823fe22e38c429cba9513"},
+    {file = "memray-1.14.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:72febec7b287667e8ea9ee3e879a4da19a4318bc47e211da815be74acd961994"},
+    {file = "memray-1.14.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e07bdc3a4979b335c2b6b93a81b807d5aacd8dbbea56c41c6899a8bc0d2beb3"},
+    {file = "memray-1.14.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b5e729d03caf426dc45a258270537a603794ecc067ccfd92f9c67ba9332e788"},
+    {file = "memray-1.14.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1d0a1397b5387b75dc9d9759beb022cb360948584840e850474d7d39ad267f85"},
+    {file = "memray-1.14.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:c119b600e7c665e0713f09e25f9ee09733a98035688ecc1ec8fd168fa37a77f6"},
+    {file = "memray-1.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:29a2e7d84d1652ef4664bcceb155630979b4797180b70da35525d963a4ca707f"},
+    {file = "memray-1.14.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b3b8d46b6447cdecba3ba100d47c62e78cdad58b00b2d6ba004d6bad318c8668"},
+    {file = "memray-1.14.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57f9bf3f1c648f1ea877a84c21c449fdafd8cc105763ada6023e36bae9b45eb8"},
+    {file = "memray-1.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b7a59346d242fc39041d87a71cb6cf45baf492ffbb69da9690de49346be64a8"},
+    {file = "memray-1.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:11fb00105572b70f2aca8b787ce9748b0c94672fbb6334f1604f7f813ca3dca6"},
+    {file = "memray-1.14.0.tar.gz", hash = "sha256:b5d8874b7b215551f0ae9fa8aef3f2f52321a6460dc0141aaf9374709e6b0eb7"},
+]
+
+[package.dependencies]
+jinja2 = ">=2.9"
+rich = ">=11.2.0"
+textual = ">=0.41.0"
+
+[package.extras]
+benchmark = ["asv"]
+dev = ["Cython", "asv", "black", "bump2version", "check-manifest", "flake8", "furo", "greenlet", "ipython", "isort", "mypy", "packaging", "pytest", "pytest-cov", "pytest-textual-snapshot", "setuptools", "sphinx", "sphinx-argparse", "textual (>=0.43,!=0.65.2,!=0.66)", "towncrier"]
+docs = ["IPython", "bump2version", "furo", "sphinx", "sphinx-argparse", "towncrier"]
+lint = ["black", "check-manifest", "flake8", "isort", "mypy"]
+test = ["Cython", "greenlet", "ipython", "packaging", "pytest", "pytest-cov", "pytest-textual-snapshot", "setuptools", "textual (>=0.43,!=0.65.2,!=0.66)"]
+
 [[package]]
 name = "packaging"
 version = "24.1"
@@ -428,6 +651,20 @@ files = [
 [package.dependencies]
 typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
 
+[[package]]
+name = "pygments"
+version = "2.18.0"
+description = "Pygments is a syntax highlighting package written in Python."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
+    {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
+]
+
+[package.extras]
+windows-terminal = ["colorama (>=0.4.6)"]
+
 [[package]]
 name = "pyproject-api"
 version = "1.8.0"
@@ -441,7 +678,6 @@ files = [
 
 [package.dependencies]
 packaging = ">=24.1"
-tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
 
 [package.extras]
 docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"]
@@ -491,11 +727,9 @@ files = [
 
 [package.dependencies]
 colorama = {version = "*", markers = "sys_platform == \"win32\""}
-exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
 iniconfig = "*"
 packaging = "*"
 pluggy = ">=1.5,<2"
-tomli = {version = ">=1", markers = "python_version < \"3.11\""}
 
 [package.extras]
 dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
@@ -518,6 +752,108 @@ pytest = ">=4.6"
 [package.extras]
 testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
 
+[[package]]
+name = "pytest-memray"
+version = "1.7.0"
+description = "A simple plugin to use with pytest"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pytest_memray-1.7.0-py3-none-any.whl", hash = "sha256:b896718c1adf6d0cd339dfaaaa5620f035c9919e1199a79b3453804a1254306f"},
+    {file = "pytest_memray-1.7.0.tar.gz", hash = "sha256:c18fa907d2210b42f4096c093e2d3416dfc002dcaa450ef3f9ba819bc3dd8f5f"},
+]
+
+[package.dependencies]
+memray = ">=1.12"
+pytest = ">=7.2"
+
+[package.extras]
+docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinx-inline-tabs (>=2022.1.2b11)", "sphinxcontrib-programoutput (>=0.17)", "towncrier (>=22.12)"]
+lint = ["black (==22.12)", "isort (==5.11.4)", "mypy (==0.991)", "ruff (==0.0.272)"]
+test = ["anyio (>=4.4.0)", "covdefaults (>=2.2.2)", "coverage (>=7.0.5)", "flaky (>=3.7)", "pytest (>=7.2)", "pytest-xdist (>=3.1)"]
+
+[[package]]
+name = "pytest-mock"
+version = "3.14.0"
+description = "Thin-wrapper around the mock package for easier use with pytest"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"},
+    {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"},
+]
+
+[package.dependencies]
+pytest = ">=6.2.5"
+
+[package.extras]
+dev = ["pre-commit", "pytest-asyncio", "tox"]
+
+[[package]]
+name = "pytest-qt"
+version = "4.4.0"
+description = "pytest support for PyQt and PySide applications"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pytest-qt-4.4.0.tar.gz", hash = "sha256:76896142a940a4285339008d6928a36d4be74afec7e634577e842c9cc5c56844"},
+    {file = "pytest_qt-4.4.0-py3-none-any.whl", hash = "sha256:001ed2f8641764b394cf286dc8a4203e40eaf9fff75bf0bfe5103f7f8d0c591d"},
+]
+
+[package.dependencies]
+pluggy = ">=1.1"
+pytest = "*"
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+doc = ["sphinx", "sphinx-rtd-theme"]
+
+[[package]]
+name = "pytest-xdist"
+version = "3.6.1"
+description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
+    {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
+]
+
+[package.dependencies]
+execnet = ">=2.1"
+pytest = ">=7.0.0"
+
+[package.extras]
+psutil = ["psutil (>=3.0)"]
+setproctitle = ["setproctitle"]
+testing = ["filelock"]
+
+[[package]]
+name = "pytest-xvfb"
+version = "3.0.0"
+description = "A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pytest-xvfb-3.0.0.tar.gz", hash = "sha256:3746ab1f4d1159f03f751638d053689ccd284291b38b8fb03d3ebbe7bf69cfc0"},
+    {file = "pytest_xvfb-3.0.0-py3-none-any.whl", hash = "sha256:352f247c788457ccdfcfeec8a47a2a6594c8eaf22f0302dae9e2635bb23975c2"},
+]
+
+[package.dependencies]
+pytest = ">=2.8.1"
+pyvirtualdisplay = ">=1.3"
+
+[[package]]
+name = "pyvirtualdisplay"
+version = "3.0"
+description = "python wrapper for Xvfb, Xephyr and Xvnc"
+optional = false
+python-versions = "*"
+files = [
+    {file = "PyVirtualDisplay-3.0-py3-none-any.whl", hash = "sha256:40d4b8dfe4b8de8552e28eb367647f311f88a130bf837fe910e7f180d5477f0e"},
+    {file = "PyVirtualDisplay-3.0.tar.gz", hash = "sha256:09755bc3ceb6eb725fb07eca5425f43f2358d3bf08e00d2a9b792a1aedd16159"},
+]
+
 [[package]]
 name = "pyyaml"
 version = "6.0.2"
@@ -580,6 +916,24 @@ files = [
     {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
 ]
 
+[[package]]
+name = "rich"
+version = "13.8.1"
+description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+    {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"},
+    {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=2.2.0"
+pygments = ">=2.13.0,<3.0.0"
+
+[package.extras]
+jupyter = ["ipywidgets (>=7.5.1,<9)"]
+
 [[package]]
 name = "ruff"
 version = "0.6.9"
@@ -620,6 +974,17 @@ files = [
     {file = "shiboken6-6.8.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:3f2601c0db8d1e860a2a1265936e616361b073360eb15153806638fbc3b7ff26"},
 ]
 
+[[package]]
+name = "textual"
+version = "0.79.1"
+description = "Modern Text User Interface framework"
+optional = false
+python-versions = "<4.0.0,>=3.8.1"
+files = [
+    {file = "textual-0.79.1-py3-none-any.whl", hash = "sha256:75f26c0a8829560a1a8cc739f758c2c1c684246e27166acb3f4ad40110200692"},
+    {file = "textual-0.79.1.tar.gz", hash = "sha256:2aaa9778beac5e56957794ee492bd8d281d39516ccb0e507e726484a1327d8b2"},
+]
+
 [[package]]
 name = "tomli"
 version = "2.0.2"
@@ -631,6 +996,15 @@ files = [
     {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
 ]
 
+[package.dependencies]
+markdown-it-py = {version = ">=2.1.0", extras = ["linkify", "plugins"]}
+platformdirs = ">=4.2.2,<5.0.0"
+rich = ">=13.3.3"
+typing-extensions = ">=4.4.0,<5.0.0"
+
+[package.extras]
+syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree-sitter-languages (==1.10.2)"]
+
 [[package]]
 name = "tox"
 version = "4.23.0"
@@ -651,6 +1025,12 @@ packaging = ">=24.1"
 platformdirs = ">=4.3.6"
 pluggy = ">=1.5"
 pyproject-api = ">=1.8"
+virtualenv = ">=20.26.6"
+
+[package.extras]
+docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-argparse-cli (>=1.17)", "sphinx-autodoc-typehints (>=2.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=24.8)"]
+testing = ["build[virtualenv] (>=1.2.2)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=74.1.2)", "time-machine (>=2.15)", "wheel (>=0.44)"]
+pyproject-api = ">=1.8"
 tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
 typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""}
 virtualenv = ">=20.26.6"
@@ -666,6 +1046,20 @@ files = [
     {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
 ]
 
+[[package]]
+name = "uc-micro-py"
+version = "1.0.3"
+description = "Micro subset of unicode data files for linkify-it-py projects."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"},
+    {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"},
+]
+
+[package.extras]
+test = ["coverage", "pytest", "pytest-cov"]
+
 [[package]]
 name = "virtualenv"
 version = "20.27.0"
@@ -688,5 +1082,5 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
 
 [metadata]
 lock-version = "2.0"
-python-versions = ">=3.10,<3.13"
-content-hash = "0041a68424b017ace03091c4da888fc65d66a81ca211759c4c8df52a56321df2"
+python-versions = ">=3.12,<3.13"
+content-hash = "cf78527c3a52ce9c5ee2ac2abdd9795fe50cdc772bfbdba73fd5b532fa8ca24c"
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 74b01d4b..915db8aa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,7 +30,7 @@ classifiers= [
 ]
 
 [tool.poetry.dependencies]
-python = ">=3.10,<3.13"
+python = ">=3.12,<3.13"
 pyside6-essentials = "^6.7.2"
 pydantic = "^2.8.2"
 pyyaml = "^6.0.2"
@@ -38,8 +38,13 @@ pillow = "^10.4.0 || ^11.0.0"
 pytesseract = "^0.3.13"
 
 [tool.poetry.group.dev.dependencies]
-pytest = "^7.1.3 || ^8.0.0"
+pytest = "^8.3.3"
 pytest-cov = "^4.1.0 || ^5.0.0"
+pytest-qt = "^4.4.0"
+pytest-xvfb = "^3.0.0"
+pytest-mock = "^3.14.0"
+pytest-memray = "^1.7.0"
+pytest-xdist = "^3.6.1"
 ruff = "^0.2.0 || ^0.3.0 || ^0.6.0"
 tox = "^4.0.0"
 
@@ -52,6 +57,7 @@ select = ["E", "F", "W", "I", "S", "B", "UP"]
 
 [tool.ruff.lint.per-file-ignores]
 "gi_loadouts/*" = ["E501"]
+"test/*" = ["S101", "E501", "S311"]
 
 [build-system]
 requires = ["poetry-core"]
diff --git a/tox.ini b/tox.ini
index f703088a..8778a116 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 minversion = 3.12.0
-envlist = py{312},cleaning
+envlist = py{312},py{312}-dist,cleaning
 isolated_build = true
 skip_missing_interpreters = true
 
@@ -9,10 +9,26 @@ setenv =
     PYTHONPATH={toxinidir}
 skip_install = true
 sitepackages = false
-allowlist_externals = poetry
+allowlist_externals =
+    poetry
 commands_pre =
     pip3 install --upgrade poetry
     poetry install --all-extras
+commands =
+    poetry run pytest -o "addopts=--cov-config .coveragerc --cov=gi_loadouts --cov-report term-missing --cov-report xml --cov-report html" test/ {posargs} -vvvv
+
+[testenv:py{312}-dist]
+setenv =
+    PYTHONPATH={toxinidir}
+skip_install = true
+sitepackages = false
+allowlist_externals =
+    poetry
+commands_pre =
+    pip3 install --upgrade poetry
+    poetry install --all-extras
+commands =
+    poetry run pytest -o "addopts=--numprocesses auto --memray --cov-config .coveragerc --cov=gi_loadouts --cov-report term-missing --cov-report xml --cov-report html" test/ {posargs} -vvvv
 
 [testenv:cleaning]
 commands =


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..9fa1e434
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,44 @@
+---
+name: Checking code functionality
+on: [push]
+jobs:
+  ci-test:
+    runs-on: ubuntu-latest
+    container: fedora:latest
+    strategy:
+      fail-fast: false
+      matrix:
+        tox-env: ["py312-dist"]
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Update existing packages
+        run: |
+             dnf update --refresh --assumeyes
+
+      - name: Install the supporting dependencies
+        run: |
+             dnf install \
+                 libglvnd-egl \
+                 dbus-libs \
+                 libxkbcommon-x11 \
+                 libxkbcommon \
+                 xcb-util \
+                 xcb-util-image \
+                 xcb-util-renderutil \
+                 xcb-util-cursor \
+                 xcb-util-keysyms \
+                 xcb-util-wm \
+                 xorg-x11-server-Xvfb \
+                 tesseract \
+                 python3-pip \
+             --assumeyes \
+             --setopt=install_weak_deps=False
+
+      - name: Install the base dependencies
+        run: |
+             python3 -m pip install --upgrade poetry tox
+
+      - name: Check the functionality of the code
+        run: |
+             tox -e ${{ matrix.tox-env }}
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 00000000..27ca376a
--- /dev/null
+++ b/test/__init__.py
@@ -0,0 +1,7 @@
+json_type = "GOOD Files (*.json)"
+
+yaml_type = "YAML Files (*.yaml)"
+
+
+def verify_accuracy(data: float, base: float, perc: float = 5.0):
+    return abs((base - data) * 100 / base) <= perc
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
index 00000000..3da7f3ba
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,29 @@
+import pytest
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts.face.scan.main import ScanDialog
+from gi_loadouts.face.wind.main import MainWindow
+
+
+@pytest.fixture
+def runner(qtbot: QtBot) -> MainWindow:
+    """
+    Fixture for MainWindow class
+
+    :return:
+    """
+    testwind = MainWindow()
+    qtbot.addWidget(testwind)
+    return testwind
+
+
+@pytest.fixture
+def scantest(qtbot: QtBot) -> ScanDialog:
+    """
+    Fixture for ScanDialog class
+
+    :return:
+    """
+    testscan = ScanDialog("fwol")
+    qtbot.addWidget(testscan)
+    return testscan
diff --git a/gi_loadouts/type/weap/base/__init__.py b/test/data/__init__.py
similarity index 100%
rename from gi_loadouts/type/weap/base/__init__.py
rename to test/data/__init__.py
diff --git a/test/data/test_arti.py b/test/data/test_arti.py
new file mode 100644
index 00000000..44541bf8
--- /dev/null
+++ b/test/data/test_arti.py
@@ -0,0 +1,719 @@
+import pytest
+
+from gi_loadouts.data.arti import __artilist__
+from gi_loadouts.type.rare import Rare
+from gi_loadouts.type.stat import ATTR, STAT
+
+
+@pytest.mark.parametrize(
+    "name, pair, quad, rare, team",
+    [
+        pytest.param(
+            "Adventurer",
+            [ATTR(stat_name=STAT.health_points, stat_data=1000)],
+            [],
+            [1, 2, 3],
+            (
+                "Adventurer's Flower",
+                "Adventurer's Tail Feather",
+                "Adventurer's Pocket Watch",
+                "Adventurer's Golden Goblet",
+                "Adventurer's Bandana"
+            ),
+            id="data.arti: Adventurer"
+        ),
+        pytest.param(
+            "Archaic Petra",
+            [ATTR(stat_name=STAT.damage_bonus_geo_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Flower of Creviced Cliff",
+                "Feather of Jagged Peaks",
+                "Sundial of Enduring Jade",
+                "Goblet of Chiseled Crag",
+                "Mask of Solitude Basalt"
+            ),
+            id="data.arti: Archaic Petra"
+        ),
+        pytest.param(
+            "Berserker",
+            [ATTR(stat_name=STAT.critical_rate_perc, stat_data=12)],
+            [],
+            [3, 4],
+            (
+                "Berserker's Rose",
+                "Berserker's Indigo Feather",
+                "Berserker's Timepiece",
+                "Berserker's Bone Goblet",
+                "Berserker's Battle Mask"
+            ),
+            id="data.arti: Berserker"
+        ),
+        pytest.param(
+            "Blizzard Strayer",
+            [ATTR(stat_name=STAT.damage_bonus_cryo_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Snowswept Memory",
+                "Icebreaker's Resolve",
+                "Frozen Homeland's Demise",
+                "Frost-Weaved Dignity",
+                "Broken Rime's Echo"
+            ),
+            id="data.arti: Blizzard Strayer"
+        ),
+        pytest.param(
+            "Bloodstained Chivalry",
+            [ATTR(stat_name=STAT.damage_bonus_physical_perc, stat_data=25)],
+            [],
+            [4, 5],
+            (
+                "Bloodstained Flower of Iron",
+                "Bloodstained Black Plume",
+                "Bloodstained Final Hour",
+                "Bloodstained Chevalier's Goblet",
+                "Bloodstained Iron Mask"
+            ),
+            id="data.arti: Bloodstained Chivalry"
+        ),
+        pytest.param(
+            "Brave Heart",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [3, 4],
+            (
+                "Medal of the Brave",
+                "Prospect of the Brave",
+                "Fortitude of the Brave",
+                "Outset of the Brave",
+                "Crown of the Brave"
+            ),
+            id="data.arti: Brave Heart"
+        ),
+        pytest.param(
+            "Crimson Witch of Flames",
+            [ATTR(stat_name=STAT.damage_bonus_pyro_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Witch's Flower of Blaze",
+                "Witch's Ever-Burning Plume",
+                "Witch's End Time",
+                "Witch's Heart Flames",
+                "Witch's Scorching Hat"
+            ),
+            id="data.arti: Crimson Witch of Flames"
+        ),
+        pytest.param(
+            "Deepwood Memories",
+            [ATTR(stat_name=STAT.damage_bonus_dendro_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Labyrinth Wayfarer",
+                "Scholar of Vines",
+                "A Time of Insight",
+                "Lamp of the Lost",
+                "Laurel Coronet"
+            ),
+            id="data.arti: Deepwood Memories"
+        ),
+        pytest.param(
+            "Defender's Will",
+            [ATTR(stat_name=STAT.defense_perc, stat_data=30)],
+            [],
+            [3, 4],
+            (
+                "Guardian's Flower",
+                "Guardian's Sigil",
+                "Guardian's Clock",
+                "Guardian's Vessel",
+                "Guardian's Band"
+            ),
+            id="data.arti: Defender's Will"
+        ),
+        pytest.param(
+            "Desert Pavilion Chronicle",
+            [ATTR(stat_name=STAT.damage_bonus_anemo_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "The First Days of the City of Kings",
+                "End of the Golden Realm",
+                "Timepiece of the Lost Path",
+                "Defender of the Enchanting Dream",
+                "Legacy of the Desert High-Born"
+            ),
+            id="data.arti: Desert Pavilion Chronicle"
+        ),
+        pytest.param(
+            "Echoes of an Offering",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [4, 5],
+            (
+                "Soulscent Bloom",
+                "Jade Leaf",
+                "Symbol of Felicitation",
+                "Chalice of the Font",
+                "Flowing Rings"
+            ),
+            id="data.arti: Archaic Petra"
+        ),
+        pytest.param(
+            "Emblem of Severed Fate",
+            [ATTR(stat_name=STAT.energy_recharge_perc, stat_data=20)],
+            [],
+            [4, 5],
+            (
+                "Magnificent Tsuba",
+                "Sundered Feather",
+                "Storm Cage",
+                "Scarlet Vessel",
+                "Ornate Kabuto"
+            ),
+            id="data.arti: Emblem of Severed Fate"
+        ),
+        pytest.param(
+            "Flower of Paradise Lost",
+            [ATTR(stat_name=STAT.elemental_mastery, stat_data=80)],
+            [],
+            [4, 5],
+            (
+                "Ay-Khanoum's Myriad",
+                "Wilting Feast",
+                "A Moment Congealed",
+                "Secret-Keeper's Magic Bottle",
+                "Amethyst Crown"
+            ),
+            id="data.arti: Flower of Paradise Lost"
+        ),
+        pytest.param(
+            "Fragment of Harmonic Whimsy",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [4, 5],
+            (
+                "Harmonious Symphony Prelude",
+                "Ancient Sea's Nocturnal Musing",
+                "The Grand Jape of the Turning of Fate",
+                "Ichor Shower Rhapsody",
+                "Whimsical Dance of the Withered"
+            ),
+            id="data.arti: Fragment of Harmonic Whimsy"
+        ),
+        pytest.param(
+            "Gambler",
+            [],
+            [],
+            [3, 4],
+            (
+                "Gambler's Brooch",
+                "Gambler's Feather Accessory",
+                "Gambler's Pocket Watch",
+                "Gambler's Dice Cup",
+                "Gambler's Earrings"
+            ),
+            id="data.arti: Gambler"
+        ),
+        pytest.param(
+            "Gilded Dreams",
+            [ATTR(stat_name=STAT.elemental_mastery, stat_data=80)],
+            [],
+            [4, 5],
+            (
+                "Dreaming Steelbloom",
+                "Feather of Judgment",
+                "The Sunken Years",
+                "Honeyed Final Feast",
+                "Shadow of the Sand King"
+            ),
+            id="data.arti: Gilded Dreams"
+        ),
+        pytest.param(
+            "Gladiator's Finale",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [4, 5],
+            (
+                "Gladiator's Nostalgia",
+                "Gladiator's Destiny",
+                "Gladiator's Longing",
+                "Gladiator's Intoxication",
+                "Gladiator's Triumphus"
+            ),
+            id="data.arti: Gladiator's Finale"
+        ),
+        pytest.param(
+            "Golden Troupe",
+            [],
+            [],
+            [4, 5],
+            (
+                "Golden Song's Variation",
+                "Golden Bird's Shedding",
+                "Golden Era's Prelude",
+                "Golden Night's Bustle",
+                "Golden Troupe's Reward"
+            ),
+            id="data.arti: Golden Troupe"
+        ),
+        pytest.param(
+            "Heart of Depth",
+            [ATTR(stat_name=STAT.damage_bonus_hydro_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Gilded Corsage",
+                "Gust of Nostalgia",
+                "Copper Compass",
+                "Goblet of Thundering Deep",
+                "Wine-Stained Tricorne"
+            ),
+            id="data.arti: Heart of Depth"
+        ),
+        pytest.param(
+            "Husk of Opulent Dreams",
+            [ATTR(stat_name=STAT.defense_perc, stat_data=30)],
+            [],
+            [4, 5],
+            (
+                "Bloom Times",
+                "Plume of Luxury",
+                "Song of Life",
+                "Calabash of Awakening",
+                "Skeletal Hat"
+            ),
+            id="data.arti: Husk of Opulent Dreams"
+        ),
+        pytest.param(
+            "Instructor",
+            [ATTR(stat_name=STAT.elemental_mastery, stat_data=80)],
+            [],
+            [3, 4],
+            (
+                "Instructor's Brooch",
+                "Instructor's Feather Accessory",
+                "Instructor's Pocket Watch",
+                "Instructor's Tea Cup",
+                "Instructor's Cap"
+            ),
+            id="data.arti: Instructor"
+        ),
+        pytest.param(
+            "Lavawalker",
+            [ATTR(stat_name=STAT.resistance_pyro_perc, stat_data=40)],
+            [],
+            [4, 5],
+            (
+                "Lavawalker's Resolution",
+                "Lavawalker's Salvation",
+                "Lavawalker's Torment",
+                "Lavawalker's Epiphany",
+                "Lavawalker's Wisdom"
+            ),
+            id="data.arti: Lavawalker"
+        ),
+        pytest.param(
+            "Lucky Dog",
+            [ATTR(stat_name=STAT.defense, stat_data=100)],
+            [],
+            [1, 2, 3],
+            (
+                "Lucky Dog's Clover",
+                "Lucky Dog's Eagle Feather",
+                "Lucky Dog's Hourglass",
+                "Lucky Dog's Goblet",
+                "Lucky Dog's Silver Circlet"
+            ),
+            id="data.arti: Lucky Dog"
+        ),
+        pytest.param(
+            "Maiden Beloved",
+            [ATTR(stat_name=STAT.healing_bonus_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Maiden's Distant Love",
+                "Maiden's Heart-Stricken Infatuation",
+                "Maiden's Passing Youth",
+                "Maiden's Fleeting Leisure",
+                "Maiden's Fading Beauty"
+            ),
+            id="data.arti: Maiden Beloved"
+        ),
+        pytest.param(
+            "Marechaussee Hunter",
+            [],
+            [],
+            [4, 5],
+            (
+                "Hunter's Brooch",
+                "Masterpiece's Overture",
+                "Moment of Judgment",
+                "Forgotten Vessel",
+                "Veteran's Visage"
+            ),
+            id="data.arti: Marechaussee Hunter"
+        ),
+        pytest.param(
+            "Martial Artist",
+            [],
+            [],
+            [3, 4],
+            (
+                "Martial Artist's Red Flower",
+                "Martial Artist's Feather Accessory",
+                "Martial Artist's Water Hourglass",
+                "Martial Artist's Wine Cup",
+                "Martial Artist's Bandana"
+            ),
+            id="data.arti: Martial Artist"
+        ),
+        pytest.param(
+            "Nighttime Whispers in the Echoing Woods",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [4, 5],
+            (
+                "Selfless Floral Accessory",
+                "Honest Quill",
+                "Faithful Hourglass",
+                "Magnanimous Ink Bottle",
+                "Compassionate Ladies' Hat"
+            ),
+            id="data.arti: Nighttime Whispers in the Echoing Woods"
+        ),
+        pytest.param(
+            "Noblesse Oblige",
+            [],
+            [],
+            [4, 5],
+            (
+                "Royal Flora",
+                "Royal Plume",
+                "Royal Pocket Watch",
+                "Royal Silver Urn",
+                "Royal Masque"
+            ),
+            id="data.arti: Noblesse Oblige"
+        ),
+        pytest.param(
+            "Nymph's Dream",
+            [ATTR(stat_name=STAT.damage_bonus_hydro_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Odyssean Flower",
+                "Wicked Mage's Plumule",
+                "Nymph's Constancy",
+                "Heroes' Tea Party",
+                "Fell Dragon's Monocle"
+            ),
+            id="data.arti: Nymph's Dream"
+        ),
+        pytest.param(
+            "Obsidian Codex",
+            [],
+            [],
+            [4, 5],
+            (
+                "Reckoning of the Xenogenic",
+                "Root of the Spirit-Marrow",
+                "Myths of the Night Realm",
+                "Pre-Banquet of the Contenders",
+                "Crown of the Saints"
+            ),
+            id="data.arti: Obsidian Codex"
+        ),
+        pytest.param(
+            "Ocean-Hued Clam",
+            [ATTR(stat_name=STAT.healing_bonus_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Sea-Dyed Blossom",
+                "Deep Palace's Plume",
+                "Cowry of Parting",
+                "Pearl Cage",
+                "Crown of Watatsumi"
+            ),
+            id="data.arti: Ocean-Hued Clam"
+        ),
+        pytest.param(
+            "Pale Flame",
+            [ATTR(stat_name=STAT.damage_bonus_physical_perc, stat_data=25)],
+            [],
+            [4, 5],
+            (
+                "Stainless Bloom",
+                "Wise Doctor's Pinion",
+                "Moment of Cessation",
+                "Surpassing Cup",
+                "Mocking Mask"
+            ),
+            id="data.arti: Pale Flame"
+        ),
+        pytest.param(
+            "Resolution of Sojourner",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [3, 4],
+            (
+                "Heart of Comradeship",
+                "Feather of Homecoming",
+                "Sundial of the Sojourner",
+                "Goblet of the Sojourner",
+                "Crown of Parting"
+            ),
+            id="data.arti: Resolution of Sojourner"
+        ),
+        pytest.param(
+            "Retracing Bolide",
+            [ATTR(stat_name=STAT.shield_strength_perc, stat_data=35)],
+            [],
+            [4, 5],
+            (
+                "Summer Night's Bloom",
+                "Summer Night's Finale",
+                "Summer Night's Moment",
+                "Summer Night's Waterballoon",
+                "Summer Night's Mask"
+            ),
+            id="data.arti: Retracing Bolide"
+        ),
+        pytest.param(
+            "Scholar",
+            [ATTR(stat_name=STAT.energy_recharge_perc, stat_data=20)],
+            [],
+            [3, 4],
+            (
+                "Scholar's Bookmark",
+                "Scholar's Quill Pen",
+                "Scholar's Clock",
+                "Scholar's Ink Cup",
+                "Scholar's Lens"
+            ),
+            id="data.arti: Scholar"
+        ),
+        pytest.param(
+            "Scroll of the Hero of Cinder City",
+            [],
+            [],
+            [4, 5],
+            (
+                "Beast Tamer's Talisman",
+                "Mountain Ranger's Marker",
+                "Mystic's Gold Dial",
+                "Wandering Scholar's Claw Cup",
+                "Demon-Warrior's Feather Mask"
+            ),
+            id="data.arti: Scroll of the Hero of Cinder City"
+        ),
+        pytest.param(
+            "Shimenawa's Reminiscence",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [4, 5],
+            (
+                "Entangling Bloom",
+                "Shaft of Remembrance",
+                "Morning Dew's Moment",
+                "Hopeful Heart",
+                "Capricious Visage"
+            ),
+            id="data.arti: Shimenawa's Reminiscence"
+        ),
+        pytest.param(
+            "Song of Days Past",
+            [ATTR(stat_name=STAT.healing_bonus_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Forgotten Oath of Days Past",
+                "Recollection of Days Past",
+                "Echoing Sound From Days Past",
+                "Promised Dream of Days Past",
+                "Poetry of Days Past"
+            ),
+            id="data.arti: Song of Days Past"
+        ),
+        pytest.param(
+            "Tenacity of the Millelith",
+            [ATTR(stat_name=STAT.health_points_perc, stat_data=20)],
+            [],
+            [4, 5],
+            (
+                "Flower of Accolades",
+                "Ceremonial War-Plume",
+                "Orichalceous Time-Dial",
+                "Noble's Pledging Vessel",
+                "General's Ancient Helm"
+            ),
+            id="data.arti: Tenacity of the Millelith"
+        ),
+        pytest.param(
+            "The Exile",
+            [ATTR(stat_name=STAT.energy_recharge_perc, stat_data=20)],
+            [],
+            [3, 4],
+            (
+                "Exile's Flower",
+                "Exile's Feather",
+                "Exile's Pocket Watch",
+                "Exile's Goblet",
+                "Exile's Circlet"
+            ),
+            id="data.arti: The Exile"
+        ),
+        pytest.param(
+            "Thundering Fury",
+            [ATTR(stat_name=STAT.damage_bonus_electro_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "Thunderbird's Mercy",
+                "Survivor of Catastrophe",
+                "Hourglass of Thunder",
+                "Omen of Thunderstorm",
+                "Thunder Summoner's Crown"
+            ),
+            id="data.arti: Thundering Fury"
+        ),
+        pytest.param(
+            "Thundersoother",
+            [ATTR(stat_name=STAT.resistance_electro_perc, stat_data=40)],
+            [],
+            [4, 5],
+            (
+                "Thundersoother's Heart",
+                "Thundersoother's Plume",
+                "Hour of Soothing Thunder",
+                "Thundersoother's Goblet",
+                "Thundersoother's Diadem"
+            ),
+            id="data.arti: Thundersoother"
+        ),
+        pytest.param(
+            "Tiny Miracle",
+            [
+                ATTR(stat_name=STAT.resistance_anemo_perc, stat_data=20),
+                ATTR(stat_name=STAT.resistance_cryo_perc, stat_data=20),
+                ATTR(stat_name=STAT.resistance_dendro_perc, stat_data=20),
+                ATTR(stat_name=STAT.resistance_electro_perc, stat_data=20),
+                ATTR(stat_name=STAT.resistance_geo_perc, stat_data=20),
+                ATTR(stat_name=STAT.resistance_hydro_perc, stat_data=20),
+                ATTR(stat_name=STAT.resistance_pyro_perc, stat_data=20)
+            ],
+            [],
+            [3, 4],
+            (
+                "Tiny Miracle's Flower",
+                "Tiny Miracle's Feather",
+                "Tiny Miracle's Hourglass",
+                "Tiny Miracle's Goblet",
+                "Tiny Miracle's Earrings"
+            ),
+            id="data.arti: Tiny Miracle"
+        ),
+        pytest.param(
+            "Traveling Doctor",
+            [ATTR(stat_name=STAT.incoming_healing_bonus_perc, stat_data=20)],
+            [],
+            [1, 2, 3],
+            (
+                "Traveling Doctor's Silver Lotus",
+                "Traveling Doctor's Owl Feather",
+                "Traveling Doctor's Pocket Watch",
+                "Traveling Doctor's Medicine Pot",
+                "Traveling Doctor's Handkerchief"
+            ),
+            id="data.arti: Traveling Doctor"
+        ),
+        pytest.param(
+            "Unfinished Reverie",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [4, 5],
+            (
+                "Dark Fruit of Bright Flowers",
+                "Faded Emerald Tail",
+                "Moment of Attainment",
+                "The Wine-Flask Over Which the Plan Was Hatched",
+                "Crownless Crown"
+            ),
+            id="data.arti: Unfinished Reverie"
+        ),
+        pytest.param(
+            "Vermillion Hereafter",
+            [ATTR(stat_name=STAT.attack_perc, stat_data=18)],
+            [],
+            [4, 5],
+            (
+                "Flowering Life",
+                "Feather of Nascent Light",
+                "Solar Relic",
+                "Moment of the Pact",
+                "Thundering Poise"
+            ),
+            id="data.arti: Vermillion Hereafter"
+        ),
+        pytest.param(
+            "Viridescent Venerer",
+            [ATTR(stat_name=STAT.damage_bonus_anemo_perc, stat_data=15)],
+            [],
+            [4, 5],
+            (
+                "In Remembrance of Viridescent Fields",
+                "Viridescent Arrow Feather",
+                "Viridescent Venerer's Determination",
+                "Viridescent Venerer's Vessel",
+                "Viridescent Venerer's Diadem"
+            ),
+            id="data.arti: Viridescent Venerer"
+        ),
+        pytest.param(
+            "Vourukasha's Glow",
+            [ATTR(stat_name=STAT.health_points_perc, stat_data=20)],
+            [],
+            [4, 5],
+            (
+                "Stamen of Khvarena's Origin",
+                "Vibrant Pinion",
+                "Ancient Abscission",
+                "Feast of Boundless Joy",
+                "Heart of Khvarena's Brilliance"
+            ),
+            id="data.arti: Vourukasha's Glow"
+        ),
+        pytest.param(
+            "Wanderer's Troupe",
+            [ATTR(stat_name=STAT.elemental_mastery, stat_data=80)],
+            [],
+            [4, 5],
+            (
+                "Troupe's Dawnlight",
+                "Bard's Arrow Feather",
+                "Concert's Final Hour",
+                "Wanderer's String-Kettle",
+                "Conductor's Top Hat"
+            ),
+            id="data.arti: Wanderer's Troupe"
+        ),
+    ]
+)
+def test_arti(name: str, pair: list, quad: list, rare: list, team: list) -> None:
+    """
+    Test all artifacts for correctness
+
+    :return:
+    """
+    unit = __artilist__[name]
+    assert name == unit.name
+    assert pair == unit.pairdata
+    assert quad == unit.quaddata
+    assert [getattr(Rare, f"Star_{indx}") for indx in rare] == unit.rare
+    for indx, item in enumerate(["fwol", "pmod", "sdoe", "gboe", "ccol"]):
+        assert team[indx] == getattr(unit, item).__name__
diff --git a/test/data/test_char.py b/test/data/test_char.py
new file mode 100644
index 00000000..24b5ead2
--- /dev/null
+++ b/test/data/test_char.py
@@ -0,0 +1,644 @@
+import pytest
+
+from gi_loadouts.data.char import __charmaps__
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.rare import Rare
+from gi_loadouts.type.stat import STAT
+from gi_loadouts.type.vson import Vision
+from gi_loadouts.type.weap import WeaponType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, weap, rare, vson, seco, data",
+    [
+        pytest.param(
+            "Aether", "sword", 5, "none", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (5012.89, 97.91, 314.61, 6.0),
+                "Level 80/90 (Rank 6)": (10121.78, 197.69, 635.25, 24.0),
+            }, id="data.char: Aether",
+        ),
+        pytest.param(
+            "Albedo", "sword", 5, "geo", STAT.damage_bonus_geo_perc,
+            {
+                "Level 40/50 (Rank 2)": (5944.44, 112.88, 393.80, 7.2),
+                "Level 80/90 (Rank 6)": (12295.87, 233.48, 814.56, 28.8),
+            }, id="data.char: Albedo",
+        ),
+        pytest.param(
+            "Alhaitham", "sword", 5, "dendro", STAT.damage_bonus_dendro_perc,
+            {
+                "Level 40/50 (Rank 2)": (5999.48, 140.83, 351.31, 7.2),
+                "Level 80/90 (Rank 6)": (12409.72, 291.30, 726.67, 28.8),
+            }, id="data.char: Alhaitham",
+        ),
+        pytest.param(
+            "Aloy", "bow", 5, "cryo", STAT.damage_bonus_cryo_perc,
+            {
+                "Level 40/50 (Rank 2)": (4898.66, 105.14, 303.99, 7.2),
+                "Level 80/90 (Rank 6)": (10132.71, 217.47, 628.78, 28.8),
+            }, id="data.char: Aloy",
+        ),
+        pytest.param(
+            "Amber", "bow", 4, "pyro", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4361.22, 102.80, 276.86, 6.0),
+                "Level 80/90 (Rank 6)": (8805.94, 207.57, 559.02, 24.0),
+            }, id="data.char: Amber",
+        ),
+        pytest.param(
+            "Arataki Itto", "claymore", 5, "geo", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (5779.31, 102.13, 431.11, 4.8),
+                "Level 80/90 (Rank 6)": (11954.32, 211.25, 891.73, 19.2),
+            }, id="data.char: Arataki Itto",
+        ),
+        pytest.param(
+            "Arlecchino", "polearm", 5, "pyro", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (5889.40, 153.73, 343.71, 9.6),
+                "Level 80/90 (Rank 6)": (12182.02, 317.98, 710.96, 38.4),
+            }, id="data.char: Arlecchino",
+        ),
+        pytest.param(
+            "Baizhu", "catalyst", 5, "dendro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5999.48, 86.54, 224.53, 7.2),
+                "Level 80/90 (Rank 6)": (12409.72, 179.00, 464.44, 28.8),
+            }, id="data.char: Baizhu",
+        ),
+        pytest.param(
+            "Barbara", "catalyst", 4, "hydro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (4511.60, 73.43, 308.32, 6.0),
+                "Level 80/90 (Rank 6)": (9109.60, 148.27, 622.55, 24.0),
+            }, id="data.char: Barbara",
+        ),
+        pytest.param(
+            "Beidou", "claymore", 4, "electro", STAT.damage_bonus_electro_perc,
+            {
+                "Level 40/50 (Rank 2)": (6015.47, 103.78, 298.88, 6.0),
+                "Level 80/90 (Rank 6)": (12146.13, 209.55, 603.49, 24.0),
+            }, id="data.char: Beidou",
+        ),
+        pytest.param(
+            "Bennett", "sword", 4, "pyro", STAT.energy_recharge_perc,
+            {
+                "Level 40/50 (Rank 2)": (5714.70, 88.12, 355.51, 6.7),
+                "Level 80/90 (Rank 6)": (11538.82, 177.92, 717.84, 26.8),
+            }, id="data.char: Bennett",
+        ),
+        pytest.param(
+            "Candace", "polearm", 4, "hydro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5012.89, 97.91, 314.61, 6.0),
+                "Level 80/90 (Rank 6)": (10121.78, 197.69, 635.25, 24.0),
+            }, id="data.char: Candace",
+        ),
+        pytest.param(
+            "Charlotte", "catalyst", 4, "cryo", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4962.76, 79.79, 251.69, 6.0),
+                "Level 80/90 (Rank 6)": (10020.56, 161.12, 508.20, 24.0),
+            }, id="data.char: Charlotte",
+        ),
+        pytest.param(
+            "Chevreuse", "polearm", 4, "pyro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5514.18, 89.09, 278.75, 6.0),
+                "Level 80/90 (Rank 6)": (11133.95, 179.90, 562.83, 24.0),
+            }, id="data.char: Chevreuse",
+        ),
+        pytest.param(
+            "Chiori", "sword", 5, "geo", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (5140.83, 145.13, 428.35, 4.8),
+                "Level 80/90 (Rank 6)": (10633.64, 300.19, 886.02, 19.2),
+            }, id="data.char: Chiori",
+        ),
+        pytest.param(
+            "Chongyun", "claymore", 4, "cryo", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (5063.02, 102.80, 298.88, 6.0),
+                "Level 80/90 (Rank 6)": (10222.99, 207.57, 603.49, 24.0),
+            }, id="data.char: Chongyun",
+        ),
+        pytest.param(
+            "Clorinde", "sword", 5, "electro", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (5823.35, 151.58, 352.35, 4.8),
+                "Level 80/90 (Rank 6)": (12045.40, 313.53, 728.82, 19.2),
+            }, id="data.char: Clorinde",
+        ),
+        pytest.param(
+            "Collei", "bow", 4, "dendro", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4511.60, 92.03, 276.86, 6.0),
+                "Level 80/90 (Rank 6)": (9109.60, 185.83, 559.02, 24.0),
+            }, id="data.char: Collei",
+        ),
+        pytest.param(
+            "Cyno", "polearm", 5, "electro", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (5614.19, 142.98, 386.20, 9.6),
+                "Level 80/90 (Rank 6)": (11612.76, 295.74, 798.84, 38.4),
+            }, id="data.char: Cyno",
+        ),
+        pytest.param(
+            "Dehya", "claymore", 5, "pyro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (7045.26, 119.33, 282.22, 7.2),
+                "Level 80/90 (Rank 6)": (14572.88, 246.82, 583.77, 28.8),
+            }, id="data.char: Dehya",
+        ),
+        pytest.param(
+            "Diluc", "claymore", 5, "pyro", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (5834.36, 150.50, 352.35, 4.8),
+                "Level 80/90 (Rank 6)": (12068.17, 311.31, 728.82, 19.2),
+            }, id="data.char: Diluc",
+        ),
+        pytest.param(
+            "Diona", "bow", 4, "cryo", STAT.damage_bonus_cryo_perc,
+            {
+                "Level 40/50 (Rank 2)": (4411.34, 97.91, 276.86, 6.0),
+                "Level 80/90 (Rank 6)": (8907.16, 197.69, 559.02, 24.0),
+            }, id="data.char: Diona",
+        ),
+        pytest.param(
+            "Dori", "claymore", 4, "electro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5714.70, 102.80, 333.49, 6.0),
+                "Level 80/90 (Rank 6)": (11538.82, 207.57, 673.37, 24.0),
+            }, id="data.char: Dori",
+        ),
+        pytest.param(
+            "Emilie", "polearm", 5, "dendro", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (6098.55, 150.50, 328.17, 9.6),
+                "Level 80/90 (Rank 6)": (12614.65, 311.31, 678.80, 38.4),
+            }, id="data.char: Emilie",
+        ),
+        pytest.param(
+            "Eula", "claymore", 5, "cryo", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (5944.44, 153.73, 337.49, 9.6),
+                "Level 80/90 (Rank 6)": (12295.87, 317.98, 698.09, 38.4),
+            }, id="data.char: Eula",
+        ),
+        pytest.param(
+            "Faruzan", "bow", 4, "anemo", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4411.34, 90.56, 289.45, 6.0),
+                "Level 80/90 (Rank 6)": (8907.16, 182.86, 584.43, 24.0),
+            }, id="data.char: Faruzan",
+        ),
+        pytest.param(
+            "Fischl", "bow", 4, "electro", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4235.89, 112.59, 273.71, 6.0),
+                "Level 80/90 (Rank 6)": (8552.90, 227.34, 552.67, 24.0),
+            }, id="data.char: Faruzan",
+        ),
+        pytest.param(
+            "Freminet", "claymore", 4, "cryo", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (5564.31, 117.49, 326.57, 6.0),
+                "Level 80/90 (Rank 6)": (11235.17, 237.23, 659.39, 24.0),
+            }, id="data.char: Freminet",
+        ),
+        pytest.param(
+            "Furina", "sword", 5, "hydro", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (6880.14, 109.65, 312.62, 4.8),
+                "Level 80/90 (Rank 6)": (14231.33, 226.81, 646.65, 19.2),
+            }, id="data.char: Furina",
+        ),
+        pytest.param(
+            "Gaming", "claymore", 4, "pyro", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (5263.54, 139.03, 324.05, 6.0),
+                "Level 80/90 (Rank 6)": (10627.86, 280.72, 654.31, 24.0),
+            }, id="data.char: Gaming",
+        ),
+        pytest.param(
+            "Ganyu", "bow", 5, "cryo", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (4403.29, 150.50, 283.26, 9.6),
+                "Level 80/90 (Rank 6)": (9108.05, 311.31, 585.91, 38.4),
+            }, id="data.char: Ganyu",
+        ),
+        pytest.param(
+            "Gorou", "bow", 4, "geo", STAT.damage_bonus_geo_perc,
+            {
+                "Level 40/50 (Rank 2)": (4411.34, 84.20, 298.88, 6.0),
+                "Level 80/90 (Rank 6)": (8907.16, 170.01, 603.49, 24.0),
+            }, id="data.char: Gorou",
+        ),
+        pytest.param(
+            "Hu Tao", "polearm", 5, "pyro", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (6990.22, 47.84, 393.80, 9.6),
+                "Level 80/90 (Rank 6)": (14459.03, 98.95, 814.56, 38.4),
+            }, id="data.char: Hu Tao",
+        ),
+        pytest.param(
+            "Jean", "sword", 5, "anemo", STAT.healing_bonus_perc,
+            {
+                "Level 40/50 (Rank 2)": (6604.93, 107.50, 345.44, 5.5),
+                "Level 80/90 (Rank 6)": (13662.08, 222.36, 714.53, 22.0),
+            }, id="data.char: Jean",
+        ),
+        pytest.param(
+            "Kachina", "polearm", 4, "geo", STAT.damage_bonus_geo_perc,
+            {
+                "Level 40/50 (Rank 2)": (5438.99, 99.86, 365.27, 6.0),
+                "Level 80/90 (Rank 6)": (10982.13, 201.64, 737.53, 24.0),
+            }, id="data.char: Kachina",
+        ),
+        pytest.param(
+            "Kaedehara Kazuha", "sword", 5, "anemo", STAT.elemental_mastery,
+            {
+                "Level 40/50 (Rank 2)": (5999.48, 133.30, 362.71, 28.8),
+                "Level 80/90 (Rank 6)": (12409.72, 275.73, 750.25, 115.2),
+            }, id="data.char: Kaedehara Kazuha",
+        ),
+        pytest.param(
+            "Kaeya", "sword", 4, "cryo", STAT.energy_recharge_perc,
+            {
+                "Level 40/50 (Rank 2)": (5363.79, 102.80, 364.95, 6.7),
+                "Level 80/90 (Rank 6)": (10830.30, 207.57, 736.89, 26.8),
+            }, id="data.char: Kaeya",
+        ),
+        pytest.param(
+            "Kamisato Ayaka", "sword", 5, "cryo", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (5779.31, 153.73, 352.35, 9.6),
+                "Level 80/90 (Rank 6)": (11954.32, 317.98, 728.82, 38.4),
+            }, id="data.char: Kamisato Ayaka",
+        ),
+        pytest.param(
+            "Kamisato Ayato", "sword", 5, "hydro", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (6164.60, 134.38, 345.44, 9.6),
+                "Level 80/90 (Rank 6)": (12751.27, 277.96, 714.53, 38.4),
+            }, id="data.char: Kamisato Ayato",
+        ),
+        pytest.param(
+            "Kaveh", "claymore", 4, "dendro", STAT.elemental_mastery,
+            {
+                "Level 40/50 (Rank 2)": (5514.18, 107.70, 346.08, 24.0),
+                "Level 80/90 (Rank 6)": (11133.95, 217.46, 698.78, 96.0),
+            }, id="data.char: Kaveh",
+        ),
+        pytest.param(
+            "Keqing", "sword", 5, "electro", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (5889.40, 145.13, 359.26, 9.6),
+                "Level 80/90 (Rank 6)": (12182.02, 300.19, 743.11, 38.4),
+            }, id="data.char: Keqing",
+        ),
+        pytest.param(
+            "Kirara", "sword", 4, "dendro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5614.44, 102.80, 251.69, 6.0),
+                "Level 80/90 (Rank 6)": (11336.39, 207.57, 508.20, 24.0),
+            }, id="data.char: Kirara",
+        ),
+        pytest.param(
+            "Klee", "catalyst", 5, "pyro", STAT.damage_bonus_pyro_perc,
+            {
+                "Level 40/50 (Rank 2)": (4623.45, 139.75, 276.35, 7.2),
+                "Level 80/90 (Rank 6)": (9563.45, 289.07, 571.62, 28.8),
+            }, id="data.char: Klee",
+        ),
+        pytest.param(
+            "Kujou Sara", "bow", 4, "electro", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4411.34, 90.07, 289.45, 6.0),
+                "Level 80/90 (Rank 6)": (8907.16, 181.87, 584.43, 24.0),
+            }, id="data.char: Kujou Sara",
+        ),
+        pytest.param(
+            "Kuki Shinobu", "sword", 4, "electro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5664.57, 97.91, 346.08, 6.0),
+                "Level 80/90 (Rank 6)": (11437.61, 197.69, 698.78, 24.0),
+            }, id="data.char: Kuki Shinobu",
+        ),
+        pytest.param(
+            "Layla", "sword", 4, "cryo", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5113.15, 99.86, 302.03, 6.0),
+                "Level 80/90 (Rank 6)": (10324.21, 201.64, 609.84, 24.0),
+            }, id="data.char: Layla",
+        ),
+        pytest.param(
+            "Lisa", "catalyst", 4, "electro", STAT.elemental_mastery,
+            {
+                "Level 40/50 (Rank 2)": (4411.34, 106.72, 264.28, 24.0),
+                "Level 80/90 (Rank 6)": (8907.16, 215.48, 533.61, 96.0),
+            }, id="data.char: Lisa",
+        ),
+        pytest.param(
+            "Lumine", "sword", 5, "none", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (5012.89, 97.91, 314.61, 6.0),
+                "Level 80/90 (Rank 6)": (10121.78, 197.69, 635.25, 24.0),
+            }, id="data.char: Lumine",
+        ),
+        pytest.param(
+            "Lynette", "sword", 4, "anemo", STAT.damage_bonus_anemo_perc,
+            {
+                "Level 40/50 (Rank 2)": (5714.70, 106.72, 328.14, 6.0),
+                "Level 80/90 (Rank 6)": (11538.82, 215.48, 662.57, 24.0),
+            }, id="data.char: Lynette",
+        ),
+        pytest.param(
+            "Lyney", "bow", 5, "pyro", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (4953.70, 142.98, 241.81, 4.8),
+                "Level 80/90 (Rank 6)": (10246.56, 295.74, 500.17, 19.2),
+            }, id="data.char: Lyney",
+        ),
+        pytest.param(
+            "Mika", "polearm", 4, "cryo", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5764.82, 102.80, 328.77, 6.0),
+                "Level 80/90 (Rank 6)": (11640.04, 207.57, 663.84, 24.0),
+            }, id="data.char: Mika",
+        ),
+        pytest.param(
+            "Mona", "catalyst", 5, "hydro", STAT.energy_recharge_perc,
+            {
+                "Level 40/50 (Rank 2)": (4678.49, 129.00, 293.62, 8.0),
+                "Level 80/90 (Rank 6)": (9677.30, 266.84, 607.35, 32.0),
+            }, id="data.char: Mona",
+        ),
+        pytest.param(
+            "Mualani", "catalyst", 5, "hydro", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (6825.10, 81.70, 256.31, 4.8),
+                "Level 80/90 (Rank 6)": (14117.48, 169.00, 530.18, 19.2),
+            }, id="data.char: Mualani",
+        ),
+        pytest.param(
+            "Navia", "claymore", 5, "geo", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (5685.75, 158.03, 356.49, 9.6),
+                "Level 80/90 (Rank 6)": (11760.77, 326.88, 737.39, 38.4),
+            }, id="data.char: Navia",
+        ),
+        pytest.param(
+            "Neuvillette", "catalyst", 5, "hydro", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (6604.93, 93.63, 259.08, 9.6),
+                "Level 80/90 (Rank 6)": (13662.08, 193.68, 535.90, 38.4),
+            }, id="data.char: Neuvillette",
+        ),
+        pytest.param(
+            "Nilou", "sword", 5, "hydro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (6825.10, 103.20, 327.48, 7.2),
+                "Level 80/90 (Rank 6)": (14117.48, 213.47, 677.37, 28.8),
+            }, id="data.char: Nilou",
+        ),
+        pytest.param(
+            "Ningguang", "catalyst", 4, "geo", STAT.damage_bonus_geo_perc,
+            {
+                "Level 40/50 (Rank 2)": (4511.60, 97.91, 264.28, 6.0),
+                "Level 80/90 (Rank 6)": (9109.60, 197.69, 533.61, 24.0),
+            }, id="data.char: Ningguang",
+        ),
+        pytest.param(
+            "Noelle", "claymore", 4, "geo", STAT.defense_perc,
+            {
+                "Level 40/50 (Rank 2)": (5564.31, 88.12, 368.10, 7.5),
+                "Level 80/90 (Rank 6)": (11235.17, 177.92, 743.25, 30.0),
+            }, id="data.char: Noelle",
+        ),
+        pytest.param(
+            "Qiqi", "sword", 5, "cryo", STAT.healing_bonus_perc,
+            {
+                "Level 40/50 (Rank 2)": (5559.15, 129.00, 414.53, 5.5),
+                "Level 80/90 (Rank 6)": (11498.91, 266.84, 857.43, 22.0),
+            }, id="data.char: Qiqi",
+        ),
+        pytest.param(
+            "Raiden Shogun", "polearm", 5, "electro", STAT.energy_recharge_perc,
+            {
+                "Level 40/50 (Rank 2)": (5801.33, 151.58, 354.76, 8.0),
+                "Level 80/90 (Rank 6)": (11999.86, 313.53, 733.82, 32.0),
+            }, id="data.char: Raiden Shogun",
+        ),
+        pytest.param(
+            "Razor", "claymore", 4, "electro", STAT.damage_bonus_physical_perc,
+            {
+                "Level 40/50 (Rank 2)": (5514.18, 107.70, 346.08, 7.5),
+                "Level 80/90 (Rank 6)": (11133.95, 217.46, 698.78, 30.0),
+            }, id="data.char: Razor",
+        ),
+        pytest.param(
+            "Rosaria", "polearm", 4, "cryo", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (5664.57, 110.63, 327.20, 6.0),
+                "Level 80/90 (Rank 6)": (11437.61, 223.39, 660.66, 24.0),
+            }, id="data.char: Rosaria",
+        ),
+        pytest.param(
+            "Sangonomiya Kokomi", "catalyst", 5, "hydro", STAT.damage_bonus_hydro_perc,
+            {
+                "Level 40/50 (Rank 2)": (6054.52, 105.35, 295.35, 7.2),
+                "Level 80/90 (Rank 6)": (12523.57, 217.92, 610.92, 28.8),
+            }, id="data.char: Sangonomiya Kokomi",
+        ),
+        pytest.param(
+            "Sayu", "claymore", 4, "anemo", STAT.elemental_mastery,
+            {
+                "Level 40/50 (Rank 2)": (5464.05, 112.59, 343.24, 24.0),
+                "Level 80/90 (Rank 6)": (11032.74, 227.34, 693.06, 96.0),
+            }, id="data.char: Sayu",
+        ),
+        pytest.param(
+            "Sethos", "bow", 4, "electro", STAT.elemental_mastery,
+            {
+                "Level 40/50 (Rank 2)": (4511.60, 104.76, 257.98, 24.0),
+                "Level 80/90 (Rank 6)": (9109.60, 211.53, 520.91, 96.0),
+            }, id="data.char: Sethos",
+        ),
+        pytest.param(
+            "Shenhe", "polearm", 5, "cryo", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (5839.86, 136.53, 373.07, 7.2),
+                "Level 80/90 (Rank 6)": (12079.55, 282.40, 771.69, 28.8),
+            }, id="data.char: Shenhe",
+        ),
+        pytest.param(
+            "Shikanoin Heizou", "catalyst", 4, "anemo", STAT.damage_bonus_anemo_perc,
+            {
+                "Level 40/50 (Rank 2)": (4912.63, 103.78, 315.24, 6.0),
+                "Level 80/90 (Rank 6)": (9919.34, 209.55, 636.52, 24.0),
+            }, id="data.char: Shikanoin Heizou",
+        ),
+        pytest.param(
+            "Sigewinne", "bow", 5, "hydro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5999.48, 86.54, 224.53, 7.2),
+                "Level 80/90 (Rank 6)": (12409.72, 179.00, 464.44, 28.8),
+            }, id="data.char: Sigewinne",
+        ),
+        pytest.param(
+            "Sucrose", "catalyst", 4, "anemo", STAT.damage_bonus_anemo_perc,
+            {
+                "Level 40/50 (Rank 2)": (4260.96, 78.33, 324.05, 6.0),
+                "Level 80/90 (Rank 6)": (8603.51, 158.15, 654.31, 24.0),
+            }, id="data.char: Sucrose",
+        ),
+        pytest.param(
+            "Tartaglia", "bow", 5, "hydro", STAT.damage_bonus_hydro_perc,
+            {
+                "Level 40/50 (Rank 2)": (5889.40, 135.45, 366.16, 7.2),
+                "Level 80/90 (Rank 6)": (12182.02, 280.18, 757.40, 28.8),
+            }, id="data.char: Tartaglia",
+        ),
+        pytest.param(
+            "Thoma", "polearm", 4, "pyro", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4762.25, 93.01, 346.08, 6.0),
+                "Level 80/90 (Rank 6)": (9615.69, 187.80, 698.78, 24.0),
+            }, id="data.char: Thoma",
+        ),
+        pytest.param(
+            "Tighnari", "bow", 5, "dendro", STAT.damage_bonus_dendro_perc,
+            {
+                "Level 40/50 (Rank 2)": (4876.64, 120.40, 283.26, 7.2),
+                "Level 80/90 (Rank 6)": (10087.17, 249.05, 585.91, 28.8),
+            }, id="data.char: Tighnari",
+        ),
+        pytest.param(
+            "Venti", "bow", 5, "anemo", STAT.energy_recharge_perc,
+            {
+                "Level 40/50 (Rank 2)": (4733.53, 118.25, 300.53, 8.0),
+                "Level 80/90 (Rank 6)": (9791.15, 244.60, 621.64, 32.0),
+            }, id="data.char: Venti",
+        ),
+        pytest.param(
+            "Wanderer", "catalyst", 5, "anemo", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (4568.41, 147.28, 272.90, 4.8),
+                "Level 80/90 (Rank 6)": (9449.60, 304.64, 564.48, 19.2),
+            }, id="data.char: Wanderer",
+        ),
+        pytest.param(
+            "Wriothesley", "catalyst", 5, "cryo", STAT.critical_damage_perc,
+            {
+                "Level 40/50 (Rank 2)": (6109.56, 139.75, 343.02, 9.6),
+                "Level 80/90 (Rank 6)": (12637.42, 289.07, 709.53, 38.4),
+            }, id="data.char: Wriothesley",
+        ),
+        pytest.param(
+            "Xiangling", "polearm", 4, "pyro", STAT.elemental_mastery,
+            {
+                "Level 40/50 (Rank 2)": (5012.89, 103.78, 308.32, 24.0),
+                "Level 80/90 (Rank 6)": (10121.78, 209.55, 622.55, 96.0),
+            }, id="data.char: Xiangling",
+        ),
+        pytest.param(
+            "Xianyun", "catalyst", 5, "anemo", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4678.49, 150.50, 257.35, 7.2),
+                "Level 80/90 (Rank 6)": (9677.30, 311.31, 532.32, 28.8),
+            }, id="data.char: Xianyun",
+        ),
+        pytest.param(
+            "Xiao", "polearm", 5, "anemo", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (5724.27, 156.95, 359.26, 4.8),
+                "Level 80/90 (Rank 6)": (11840.47, 324.65, 743.11, 19.2),
+            }, id="data.char: Xiao",
+        ),
+        pytest.param(
+            "Xingqiu", "sword", 4, "hydro", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (4712.12, 93.01, 349.22, 6.0),
+                "Level 80/90 (Rank 6)": (9514.47, 187.80, 705.13, 24.0),
+            }, id="data.char: Xingqiu",
+        ),
+        pytest.param(
+            "Xinyan", "claymore", 4, "pyro", STAT.attack_perc,
+            {
+                "Level 40/50 (Rank 2)": (5163.28, 114.55, 368.10, 6.0),
+                "Level 80/90 (Rank 6)": (10425.43, 231.29, 743.25, 24.0),
+            }, id="data.char: Xinyan",
+        ),
+        pytest.param(
+            "Yae Miko", "catalyst", 5, "electro", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (4661.98, 152.65, 255.62, 4.8),
+                "Level 80/90 (Rank 6)": (9643.15, 315.76, 528.75, 19.2),
+            }, id="data.char: Yae Miko",
+        ),
+        pytest.param(
+            "Yanfei", "catalyst", 4, "pyro", STAT.damage_bonus_pyro_perc,
+            {
+                "Level 40/50 (Rank 2)": (4311.09, 110.63, 270.57, 6.0),
+                "Level 80/90 (Rank 6)": (8704.73, 223.39, 546.32, 24.0),
+            }, id="data.char: Yanfei",
+        ),
+        pytest.param(
+            "Yaoyao", "polearm", 4, "dendro", STAT.health_points_perc,
+            {
+                "Level 40/50 (Rank 2)": (5664.57, 97.91, 346.08, 6.0),
+                "Level 80/90 (Rank 6)": (11437.61, 197.69, 698.78, 24.0),
+            }, id="data.char: Yaoyao",
+        ),
+        pytest.param(
+            "Yelan", "bow", 5, "hydro", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (6494.85, 109.65, 246.30, 4.8),
+                "Level 80/90 (Rank 6)": (13434.37, 226.81, 509.46, 19.2),
+            }, id="data.char: Yelan",
+        ),
+        pytest.param(
+            "Yoimiya", "bow", 5, "pyro", STAT.critical_rate_perc,
+            {
+                "Level 40/50 (Rank 2)": (4568.41, 145.13, 276.35, 4.8),
+                "Level 80/90 (Rank 6)": (9449.60, 300.19, 571.62, 19.2),
+            }, id="data.char: Yoimiya",
+        ),
+        pytest.param(
+            "Yun Jin", "polearm", 4, "geo", STAT.energy_recharge_perc,
+            {
+                "Level 40/50 (Rank 2)": (4912.63, 88.12, 338.53, 6.7),
+                "Level 80/90 (Rank 6)": (9919.34, 177.92, 683.53, 26.8),
+            }, id="data.char: Yun Jin",
+        ),
+        pytest.param(
+            "Zhongli", "polearm", 5, "geo", STAT.damage_bonus_geo_perc,
+            {
+                "Level 40/50 (Rank 2)": (6604.93, 112.88, 331.62, 7.2),
+                "Level 80/90 (Rank 6)": (13662.08, 233.48, 685.95, 28.8),
+            }, id="data.char: Zhongli",
+        ),
+    ]
+)
+def test_char(name: str, weap: str, rare: int, vson: int, seco: STAT, data: dict) -> None:
+    """
+    Test all characters for correctness
+
+    :return:
+    """
+    unit = __charmaps__[name]()
+    assert name == unit.name
+    assert getattr(WeaponType, weap) == unit.weapon
+    assert getattr(Rare, f"Star_{rare}") == unit.rare
+    assert getattr(Vision, vson) == unit.vision
+    assert seco == unit.__statname__
+    for lvtx, qant in data.items():
+        lvut = getattr(Level, lvtx.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+        unit.levl = lvut
+        assert verify_accuracy(qant[0], unit.health_points.stat_data)
+        assert verify_accuracy(qant[1], unit.attack.stat_data)
+        assert verify_accuracy(qant[2], unit.defense.stat_data)
+        assert qant[3] == unit.seco.stat_data
diff --git a/gi_loadouts/type/weap/base/seco/__init__.py b/test/data/weap/__init__.py
similarity index 100%
rename from gi_loadouts/type/weap/base/seco/__init__.py
rename to test/data/weap/__init__.py
diff --git a/test/data/weap/test_bow.py b/test/data/weap/test_bow.py
new file mode 100644
index 00000000..0a0b2ae5
--- /dev/null
+++ b/test/data/weap/test_bow.py
@@ -0,0 +1,67 @@
+import pytest
+
+from gi_loadouts.data.weap.bows import BowsDict
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.rare import Rare
+from gi_loadouts.type.weap import Bow, Tier, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, tier, levl, batk, seco, valu",
+    [
+        pytest.param("Hunter's Bow", 1, 2, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, id="data.weap.bows: Hunter's Bow"),
+        pytest.param("Seasoned Hunter's Bow", 2, 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, id="data.weap.bows: Seasoned Hunter's Bow"),
+        pytest.param("Recurve Bow", 3, 1, "Level 80/90 (Rank 6)", 334, WeaponStatType.health_points_perc, 42.7, id="data.weap.bows: Recurve Bow"),
+        pytest.param("Slingshot", 3, 1, "Level 80/90 (Rank 6)", 334, WeaponStatType.critical_rate_perc, 28.5, id="data.weap.bows: Slingshot"),
+        pytest.param("Compound Bow", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.damage_bonus_physical_perc, 62.9, id="data.weap.bows: Compound Bow"),
+        pytest.param("Favonius Warbow", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.energy_recharge_perc, 55.9, id="data.weap.bows: Favonius Warbow"),
+        pytest.param("Hamayumi", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, id="data.weap.bows: Hamayumi"),
+        pytest.param("King's Squire", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, id="data.weap.bows: King's Squire"),
+        pytest.param("Aqua Simulacra", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, id="data.weap.bows: Aqua Simulacra"),
+        pytest.param("Hunter's Path", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_rate_perc, 40.2, id="data.weap.bows: Hunter's Path"),
+        pytest.param("Silvershower Heartstrings", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.health_points_perc, 60.3, id="data.weap.bows: Silvershower Heartstrings"),
+        pytest.param("Sharpshooter's Oath", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.critical_damage_perc, 42.7, id="data.weap.bows: Sharpshooter's Oath"),
+        pytest.param("Cloudforged", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.bows: Cloudforged"),
+        pytest.param("End of the Line", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, id="data.weap.bows: End of the Line"),
+        pytest.param("Mitternachts Waltz", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.damage_bonus_physical_perc, 47.2, id="data.weap.bows: Mitternachts Waltz"),
+        pytest.param("Predator", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.bows: Predator"),
+        pytest.param("Prototype Crescent", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.bows: Prototype Crescent"),
+        pytest.param("Royal Bow", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.bows: Royal Bow"),
+        pytest.param("Rust", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.bows: Rust"),
+        pytest.param("Song of Stillness", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.bows: Song of Stillness"),
+        pytest.param("The Stringless", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.bows: The Stringless"),
+        pytest.param("The Viridescent Hunt", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, id="data.weap.bows: The Viridescent Hunt"),
+        pytest.param("Windblume Ode", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.bows: Windblume Ode"),
+        pytest.param("Amos' Bow", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, id="data.weap.bows: Amos' Bow"),
+        pytest.param("Elegy for the End", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.energy_recharge_perc, 50.3, id="data.weap.bows: Elegy for the End"),
+        pytest.param("Polar Star", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, id="data.weap.bows: Polar Star"),
+        pytest.param("The First Great Magic", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_damage_perc, 60.3, id="data.weap.bows: The First Great Magic"),
+        pytest.param("Thundering Pulse", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_damage_perc, 60.3, id="data.weap.bows: Thundering Pulse"),
+        pytest.param("Alley Hunter", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.bows: Alley Hunter"),
+        pytest.param("Blackcliff Warbow", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.critical_damage_perc, 33.5, id="data.weap.bows: Blackcliff Warbow"),
+        pytest.param("Chain Breaker", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.bows: Chain Breaker"),
+        pytest.param("Fading Twilight", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.bows: Fading Twilight"),
+        pytest.param("Ibis Piercer", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.bows: Ibis Piercer"),
+        pytest.param("Mouun's Moon", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.bows: Mouun's Moon"),
+        pytest.param("Range Gauge", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.bows: Range Gauge"),
+        pytest.param("Sacrificial Bow", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.bows: Sacrificial Bow"),
+        pytest.param("Scion of the Blazing Sun", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.critical_rate_perc, 16.8, id="data.weap.bows: Scion of the Blazing Sun"),
+        pytest.param("Skyward Harp", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, id="data.weap.bows: Skyward Harp"),
+    ]
+)
+def test_bow(name: str, rare: int, tier: int, levl: str, batk: int, seco: WeaponStatType, valu: float) -> None:
+    """
+    Test all weapons of bow type for correctness
+
+    :return:
+    """
+    unit = BowsDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    assert unit.rare == getattr(Rare, f"Star_{rare}")
+    assert unit.tier == getattr(Tier, f"Tier_{tier}")
+    assert round(unit.main_stat.stat_data) == batk
+    assert unit.seco_stat.stat_name == seco
+    assert unit.type == Bow().type
+    if unit.seco_stat.stat_name != WeaponStatType.none:
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, valu, 1)
diff --git a/test/data/weap/test_catalyst.py b/test/data/weap/test_catalyst.py
new file mode 100644
index 00000000..c9bd3cee
--- /dev/null
+++ b/test/data/weap/test_catalyst.py
@@ -0,0 +1,69 @@
+import pytest
+
+from gi_loadouts.data.weap.catalysts import CatalystsDict
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.rare import Rare
+from gi_loadouts.type.weap import Catalyst, Tier, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, tier, levl, batk, seco, valu",
+    [
+        pytest.param("Magic Guide", 3, 1, "Level 80/90 (Rank 6)", 334, WeaponStatType.elemental_mastery, 171, id="data.weap.catalysts: Magic Guide"),
+        pytest.param("Dodoco Tales", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, id="data.weap.catalysts: Dodoco Tales"),
+        pytest.param("Eye of Perception", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, id="data.weap.catalysts: Eye of Perception"),
+        pytest.param("Sacrificial Fragments", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.elemental_mastery, 201, id="data.weap.catalysts: Sacrificial Fragments"),
+        pytest.param("Sacrificial Jade", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.critical_rate_perc, 33.5, id="data.weap.catalysts: Sacrificial Jade"),
+        pytest.param("A Thousand Floating Dreams", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.elemental_mastery, 241, id="data.weap.catalysts: A Thousand Floating Dreams"),
+        pytest.param("Surf's Up", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, id="data.weap.catalysts: Surf's Up"),
+        pytest.param("Tome of the Eternal Flow", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, id="data.weap.catalysts: Tome of the Eternal Flow"),
+        pytest.param("Apprentice's Notes", 1, 2, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, id="data.weap.catalysts: Apprentice's Notes"),
+        pytest.param("Pocket Grimoire", 2, 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, id="data.weap.catalysts: Pocket Grimoire"),
+        pytest.param("Otherworldly Story", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.energy_recharge_perc, 35.6, id="data.weap.catalysts: Otherworldly Story"),
+        pytest.param("Thrilling Tales of Dragon Slayers", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.health_points_perc, 32.1, id="data.weap.catalysts: Thrilling Tales of Dragon Slayers"),
+        pytest.param("Ash-Graven Drinking Horn", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, id="data.weap.catalysts: Ash-Graven Drinking Horn"),
+        pytest.param("Blackcliff Agate", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_damage_perc, 50.3, id="data.weap.catalysts: Blackcliff Agate"),
+        pytest.param("Favonius Codex", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, id="data.weap.catalysts: Favonius Codex"),
+        pytest.param("Frostbearer", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.catalysts: Frostbearer"),
+        pytest.param("Fruit of Fulfillment", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, id="data.weap.catalysts: Fruit of Fulfillment"),
+        pytest.param("Prototype Amber", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, id="data.weap.catalysts: Prototype Amber"),
+        pytest.param("Ring of Yaxche", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, id="data.weap.catalysts: Ring of Yaxche"),
+        pytest.param("Solar Pearl", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, id="data.weap.catalysts: Solar Pearl"),
+        pytest.param("The Widsith", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_damage_perc, 50.3, id="data.weap.catalysts: The Widsith"),
+        pytest.param("Wandering Evenstar", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.catalysts: Wandering Evenstar"),
+        pytest.param("Everlasting Moonglow", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.health_points_perc, 45.3, id="data.weap.catalysts: Everlasting Moonglow"),
+        pytest.param("Jadefall's Splendor", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.health_points_perc, 45.3, id="data.weap.catalysts: Jadefall's Splendor"),
+        pytest.param("Kagura's Verity", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_damage_perc, 60.3, id="data.weap.catalysts: Kagura's Verity"),
+        pytest.param("Lost Prayer to the Sacred Winds", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, id="data.weap.catalysts: Lost Prayer to the Sacred Winds"),
+        pytest.param("Memory of Dust", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, id="data.weap.catalysts: Memory of Dust"),
+        pytest.param("Emerald Orb", 3, 3, "Level 80/90 (Rank 6)", 415, WeaponStatType.elemental_mastery, 85, id="data.weap.catalysts: Emerald Orb"),
+        pytest.param("Twin Nephrite", 3, 3, "Level 80/90 (Rank 6)", 415, WeaponStatType.critical_rate_perc, 14.2, id="data.weap.catalysts: Twin Nephrite"),
+        pytest.param("Ballad of the Boundless Blue", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.catalysts: Ballad of the Boundless Blue"),
+        pytest.param("Flowing Purity", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.catalysts: Flowing Purity"),
+        pytest.param("Hakushin Ring", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.catalysts: Hakushin Ring"),
+        pytest.param("Mappa Mare", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.elemental_mastery, 101, id="data.weap.catalysts: Mappa Mare"),
+        pytest.param("Oathsworn Eye", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.catalysts: Oathsworn Eye"),
+        pytest.param("Royal Grimoire", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.catalysts: Royal Grimoire"),
+        pytest.param("Wine and Song", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.catalysts: Wine and Song"),
+        pytest.param("Cashflow Supervision", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, id="data.weap.catalysts: Cashflow Supervision"),
+        pytest.param("Skyward Atlas", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.attack_perc, 30.2, id="data.weap.catalysts: Skyward Atlas"),
+        pytest.param("Tulaytullah's Remembrance", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_damage_perc, 40.2, id="data.weap.catalysts: Tulaytullah's Remembrance"),
+        pytest.param("Crane's Echoing Call", 5, 4, "Level 80/90 (Rank 6)", 679, WeaponStatType.attack_perc, 15.1, id="data.weap.catalysts: Crane's Echoing Call"),
+    ]
+)
+def test_catalyst(name: str, rare: int, tier: int, levl: str, batk: int, seco: WeaponStatType, valu: float) -> None:
+    """
+    Test all weapons of catalyst type for correctness
+
+    :return:
+    """
+    unit = CatalystsDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    assert unit.rare == getattr(Rare, f"Star_{rare}")
+    assert unit.tier == getattr(Tier, f"Tier_{tier}")
+    assert round(unit.main_stat.stat_data) == batk
+    assert unit.seco_stat.stat_name == seco
+    assert unit.type == Catalyst().type
+    if unit.seco_stat.stat_name != WeaponStatType.none:
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, valu, 1.5)
diff --git a/test/data/weap/test_claymore.py b/test/data/weap/test_claymore.py
new file mode 100644
index 00000000..71af9e95
--- /dev/null
+++ b/test/data/weap/test_claymore.py
@@ -0,0 +1,65 @@
+import pytest
+
+from gi_loadouts.data.weap.claymores import ClaymoresDict
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.rare import Rare
+from gi_loadouts.type.weap import Claymore, Tier, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, tier, levl, batk, seco, valu",
+    [
+        pytest.param("Bloodtainted Greatsword", 3, 1, "Level 80/90 (Rank 6)", 334, WeaponStatType.elemental_mastery, 171, id="data.weap.claymores: Bloodtainted Greatsword"),
+        pytest.param("Portable Power Saw", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.health_points_perc, 50.3, id="data.weap.claymores: Portable Power Saw"),
+        pytest.param("Luxurious Sea-Lord", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, id="data.weap.claymores: Luxurious Sea-Lord"),
+        pytest.param("Favonius Greatsword", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.energy_recharge_perc, 55.9, id="data.weap.claymores: Favonius Greatsword"),
+        pytest.param("Redhorn Stonethresher", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, id="data.weap.claymores: Redhorn Stonethresher"),
+        pytest.param("Waster Greatsword", 1, 2, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, id="data.weap.claymores: Waster Greatsword"),
+        pytest.param("Old Merc's Pal", 2, 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, id="data.weap.claymores: Old Merc's Pal"),
+        pytest.param("White Iron Greatsword", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.defense_perc, 40.1, id="data.weap.claymores: White Iron Greatsword"),
+        pytest.param("Skyrider Greatsword", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.damage_bonus_physical_perc, 40.1, id="data.weap.claymores: Skyrider Greatsword"),
+        pytest.param("Ferrous Shadow", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.health_points_perc, 32.1, id="data.weap.claymores: Ferrous Shadow"),
+        pytest.param("Debate Club", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.attack_perc, 32.1, id="data.weap.claymores: Debate Club"),
+        pytest.param("Serpent Spine", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, id="data.weap.claymores: Serpent Spine"),
+        pytest.param("Whiteblind", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.defense_perc, 47.2, id="data.weap.claymores: Whiteblind"),
+        pytest.param("Tidal Shadow", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.claymores: Tidal Shadow"),
+        pytest.param("The Bell", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, id="data.weap.claymores: The Bell"),
+        pytest.param("Rainslasher", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.claymores: Rainslasher"),
+        pytest.param("Makhaira Aquamarine", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.claymores: Makhaira Aquamarine"),
+        pytest.param("Lithic Blade", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.claymores: Lithic Blade"),
+        pytest.param("Katsuragikiri Nagamasa", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, id="data.weap.claymores: Katsuragikiri Nagamasa"),
+        pytest.param("Blackcliff Slasher", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_damage_perc, 50.3, id="data.weap.claymores: Blackcliff Slasher"),
+        pytest.param("Akuoumaru", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.claymores: Akuoumaru"),
+        pytest.param("Beacon of the Reed Sea", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, id="data.weap.claymores: Beacon of the Reed Sea"),
+        pytest.param("Wolf's Gravestone", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, id="data.weap.claymores: Wolf's Gravestone"),
+        pytest.param("The Unforged", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, id="data.weap.claymores: The Unforged"),
+        pytest.param("Talking Stick", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.critical_rate_perc, 16.8, id="data.weap.claymores: Talking Stick"),
+        pytest.param("Snow-Tombed Starsilver", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.damage_bonus_physical_perc, 31.5, id="data.weap.claymores: Snow-Tombed Starsilver"),
+        pytest.param("Royal Greatsword", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.claymores: Royal Greatsword"),
+        pytest.param("Prototype Archaic", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.claymores: Prototype Archaic"),
+        pytest.param("Sacrificial Greatsword", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.claymores: Sacrificial Greatsword"),
+        pytest.param("Mailed Flower", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.elemental_mastery, 101, id="data.weap.claymores: Mailed Flower"),
+        pytest.param("Forest Regalia", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.claymores: Forest Regalia"),
+        pytest.param("Earth Shaker", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.claymores: Earth Shaker"),
+        pytest.param("\"Ultimate Overlord's Mega Magic Sword\"", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.claymores: \"Ultimate Overlord's Mega Magic Sword\""),
+        pytest.param("Skyward Pride", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.energy_recharge_perc, 33.5, id="data.weap.claymores: Skyward Pride"),
+        pytest.param("Verdict", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, id="data.weap.claymores: Verdict"),
+        pytest.param("Song of Broken Pines", 5, 4, "Level 80/90 (Rank 6)", 679, WeaponStatType.damage_bonus_physical_perc, 18.9, id="data.weap.claymores: Song of Broken Pines"),
+    ]
+)
+def test_claymore(name: str, rare: int, tier: int, levl: str, batk: int, seco: WeaponStatType, valu: float) -> None:
+    """
+    Test all weapons of claymore type for correctness
+
+    :return:
+    """
+    unit = ClaymoresDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    assert unit.rare == getattr(Rare, f"Star_{rare}")
+    assert unit.tier == getattr(Tier, f"Tier_{tier}")
+    assert round(unit.main_stat.stat_data) == batk
+    assert unit.seco_stat.stat_name == seco
+    assert unit.type == Claymore().type
+    if unit.seco_stat.stat_name != WeaponStatType.none:
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, valu, 1)
diff --git a/test/data/weap/test_polearm.py b/test/data/weap/test_polearm.py
new file mode 100644
index 00000000..dfd2ef5b
--- /dev/null
+++ b/test/data/weap/test_polearm.py
@@ -0,0 +1,62 @@
+import pytest
+
+from gi_loadouts.data.weap.polearms import PolearmsDict
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.rare import Rare
+from gi_loadouts.type.weap import Polearm, Tier, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, tier, levl, batk, seco, valu",
+    [
+        pytest.param("Beginner's Protector", 1, 2, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, id="data.weap.polearms: Beginner's Protector"),
+        pytest.param("Iron Point", 2, 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, id="data.weap.polearms: Iron Point"),
+        pytest.param("Black Tassel", 3, 1, "Level 80/90 (Rank 6)", 334, WeaponStatType.health_points_perc, 42.7, id="data.weap.polearms: Black Tassel"),
+        pytest.param("Deathmatch", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.critical_rate_perc, 33.5, id="data.weap.polearms: Deathmatch"),
+        pytest.param("Dragon's Bane", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.elemental_mastery, 201, id="data.weap.polearms: Dragon's Bane"),
+        pytest.param("Dragonspine Spear", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.damage_bonus_physical_perc, 62.9, id="data.weap.polearms: Dragonspine Spear"),
+        pytest.param("Staff of the Scarlet Sands", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_rate_perc, 40.2, id="data.weap.polearms: Staff of the Scarlet Sands"),
+        pytest.param("White Tassel", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.critical_rate_perc, 21.4, id="data.weap.polearms: White Tassel"),
+        pytest.param("\"The Catch\"", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, id="data.weap.polearms: \"The Catch\""),
+        pytest.param("Ballad of the Fjords", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, id="data.weap.polearms: Ballad of the Fjords"),
+        pytest.param("Blackcliff Pole", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_damage_perc, 50.3, id="data.weap.polearms: Blackcliff Pole"),
+        pytest.param("Dialogues of the Desert Sages", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, id="data.weap.polearms: Dialogues of the Desert Sages"),
+        pytest.param("Footprint of the Rainbow", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.defense_perc, 47.2, id="data.weap.polearms: Footprint of the Rainbow"),
+        pytest.param("Missive Windspear", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.polearms: Missive Windspear"),
+        pytest.param("Prototype Starglitter", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, id="data.weap.polearms: Prototype Starglitter"),
+        pytest.param("Engulfing Lightning", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.energy_recharge_perc, 50.3, id="data.weap.polearms: Engulfing Lightning"),
+        pytest.param("Lumidouce Elegy", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, id="data.weap.polearms: Lumidouce Elegy"),
+        pytest.param("Staff of Homa", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_damage_perc, 60.3, id="data.weap.polearms: Staff of Homa"),
+        pytest.param("Vortex Vanquisher", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, id="data.weap.polearms: Vortex Vanquisher"),
+        pytest.param("Halberd", 3, 3, "Level 80/90 (Rank 6)", 415, WeaponStatType.attack_perc, 21.4, id="data.weap.polearms: Halberd"),
+        pytest.param("Crescent Pike", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.damage_bonus_physical_perc, 31.5, id="data.weap.polearms: Crescent Pike"),
+        pytest.param("Favonius Lance", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.polearms: Favonius Lance"),
+        pytest.param("Kitain Cross Spear", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.elemental_mastery, 101, id="data.weap.polearms: Kitain Cross Spear"),
+        pytest.param("Lithic Spear", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.polearms: Lithic Spear"),
+        pytest.param("Moonpiercer", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.elemental_mastery, 101, id="data.weap.polearms: Moonpiercer"),
+        pytest.param("Prospector's Drill", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.polearms: Prospector's Drill"),
+        pytest.param("Rightful Reward", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.health_points_perc, 25.1, id="data.weap.polearms: Rightful Reward"),
+        pytest.param("Royal Spear", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.polearms: Royal Spear"),
+        pytest.param("Crimson Moon's Semblance", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, id="data.weap.polearms: Crimson Moon's Semblance"),
+        pytest.param("Primordial Jade Winged-Spear", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, id="data.weap.polearms: Primordial Jade Winged-Spear"),
+        pytest.param("Skyward Spine", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.energy_recharge_perc, 33.5, id="data.weap.polearms: Skyward Spine"),
+        pytest.param("Wavebreaker's Fin", 4, 4, "Level 80/90 (Rank 6)", 571, WeaponStatType.attack_perc, 12.5, id="data.weap.polearms: Wavebreaker's Fin"),
+        pytest.param("Calamity Queller", 5, 4, "Level 80/90 (Rank 6)", 679, WeaponStatType.attack_perc, 15.1, id="data.weap.polearms: Calamity Queller"),
+    ]
+)
+def test_polearm(name: str, rare: int, tier: int, levl: str, batk: int, seco: WeaponStatType, valu: float) -> None:
+    """
+    Test all weapons of polearm type for correctness
+
+    :return:
+    """
+    unit = PolearmsDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    assert unit.rare == getattr(Rare, f"Star_{rare}")
+    assert unit.tier == getattr(Tier, f"Tier_{tier}")
+    assert round(unit.main_stat.stat_data) == batk
+    assert unit.seco_stat.stat_name == seco
+    assert unit.type == Polearm().type
+    if unit.seco_stat.stat_name != WeaponStatType.none:
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, valu, 1)
diff --git a/test/data/weap/test_sword.py b/test/data/weap/test_sword.py
new file mode 100644
index 00000000..86512db5
--- /dev/null
+++ b/test/data/weap/test_sword.py
@@ -0,0 +1,73 @@
+import pytest
+
+from gi_loadouts.data.weap.swords import SwordsDict
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.rare import Rare
+from gi_loadouts.type.weap import Sword, Tier, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, tier, levl, batk, seco, valu",
+    [
+        pytest.param("Skyrider Sword", 3, 1, "Level 80/90 (Rank 6)", 334, WeaponStatType.energy_recharge_perc, 47.2, id="data.weap.swords: Skyrider Sword"),
+        pytest.param("Amenoma Kageuchi", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, id="data.weap.swords: Amenoma Kageuchi"),
+        pytest.param("Cinnabar Spindle", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.defense_perc, 62.9, id="data.weap.swords: Cinnabar Spindle"),
+        pytest.param("Favonius Sword", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.energy_recharge_perc, 55.9, id="data.weap.swords: Favonius Sword"),
+        pytest.param("Flute of Ezpitzal", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.defense_perc, 62.9, id="data.weap.swords: Flute of Ezpitzal"),
+        pytest.param("Sacrificial Sword", 4, 1, "Level 80/90 (Rank 6)", 427, WeaponStatType.energy_recharge_perc, 55.9, id="data.weap.swords: Sacrificial Sword"),
+        pytest.param("Key of Khaj-Nisut", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.health_points_perc, 60.3, id="data.weap.swords: Key of Khaj-Nisut"),
+        pytest.param("Light of Foliar Incision", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, id="data.weap.swords: Light of Foliar Incision"),
+        pytest.param("Primordial Jade Cutter", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_rate_perc, 40.2, id="data.weap.swords: Primordial Jade Cutter"),
+        pytest.param("Splendor of Tranquil Waters", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, id="data.weap.swords: Splendor of Tranquil Waters"),
+        pytest.param("Uraku Misugiri", 5, 1, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, id="data.weap.swords: Uraku Misugiri"),
+        pytest.param("Dull Blade", 1, 2, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, id="data.weap.swords: Dull Blade"),
+        pytest.param("Silver Sword", 2, 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, id="data.weap.swords: Silver Sword"),
+        pytest.param("Cool Steel", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.attack_perc, 32.1, id="data.weap.swords: Cool Steel"),
+        pytest.param("Dark Iron Sword", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.elemental_mastery, 128, id="data.weap.swords: Dark Iron Sword"),
+        pytest.param("Fillet Blade", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.attack_perc, 32.1, id="data.weap.swords: Fillet Blade"),
+        pytest.param("Harbinger of Dawn", 3, 2, "Level 80/90 (Rank 6)", 375, WeaponStatType.critical_damage_perc, 42.7, id="data.weap.swords: Harbinger of Dawn"),
+        pytest.param("Festering Desire", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, id="data.weap.swords: Festering Desire"),
+        pytest.param("Fleuve Cendre Ferryman", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, id="data.weap.swords: Fleuve Cendre Ferryman"),
+        pytest.param("Iron Sting", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.swords: Iron Sting"),
+        pytest.param("Kagotsurube Isshin", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.swords: Kagotsurube Isshin"),
+        pytest.param("Lion's Roar", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.swords: Lion's Roar"),
+        pytest.param("Royal Longsword", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.swords: Royal Longsword"),
+        pytest.param("Sword of Narzissenkreuz", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.swords: Sword of Narzissenkreuz"),
+        pytest.param("The Black Sword", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, id="data.weap.swords: The Black Sword"),
+        pytest.param("The Dockhand's Assistant", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, id="data.weap.swords: The Dockhand's Assistant"),
+        pytest.param("The Flute", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, id="data.weap.swords: The Flute"),
+        pytest.param("Toukabou Shigure", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.swords: Toukabou Shigure"),
+        pytest.param("Wolf-Fang", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, id="data.weap.swords: Wolf-Fang"),
+        pytest.param("Xiphos' Moonlight", 4, 2, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, id="data.weap.swords: Xiphos' Moonlight"),
+        pytest.param("Freedom-Sworn", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.elemental_mastery, 181, id="data.weap.swords: Freedom-Sworn"),
+        pytest.param("Haran Geppaku Futsu", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, id="data.weap.swords: Haran Geppaku Futsu"),
+        pytest.param("Skyward Blade", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.energy_recharge_perc, 50.3, id="data.weap.swords: Skyward Blade"),
+        pytest.param("Summit Shaper", 5, 2, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, id="data.weap.swords: Summit Shaper"),
+        pytest.param("Traveler's Handy Sword", 3, 3, "Level 80/90 (Rank 6)", 415, WeaponStatType.defense_perc, 26.7, id="data.weap.swords: Traveler's Handy Sword"),
+        pytest.param("Blackcliff Longsword", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.critical_damage_perc, 33.5, id="data.weap.swords: Blackcliff Longsword"),
+        pytest.param("Finale of the Deep", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, id="data.weap.swords: Finale of the Deep"),
+        pytest.param("Prototype Rancour", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.damage_bonus_physical_perc, 31.5, id="data.weap.swords: Prototype Rancour"),
+        pytest.param("Sapwood Blade", 4, 3, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, id="data.weap.swords: Sapwood Blade"),
+        pytest.param("Absolution", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_damage_perc, 40.2, id="data.weap.swords: Absolution"),
+        pytest.param("Aquila Favonia", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.damage_bonus_physical_perc, 37.7, id="data.weap.swords: Aquila Favonia"),
+        pytest.param("Mistsplitter Reforged", 5, 3, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_damage_perc, 40.2, id="data.weap.swords: Mistsplitter Reforged"),
+        pytest.param("The Alley Flash", 4, 4, "Level 80/90 (Rank 6)", 571, WeaponStatType.elemental_mastery, 50, id="data.weap.swords: The Alley Flash"),
+        #TODO pytest.param("Sword of Descension", 0, 0, "Level 80/90 (Rank 6)", 414, WeaponStatType.attack_perc, 32.1, id="data.weap.swords: Sword of Descension"),
+    ]
+)
+def test_sword(name: str, rare: int, tier: int, levl: str, batk: int, seco: WeaponStatType, valu: float) -> None:
+    """
+    Test all weapons of sword type for correctness
+
+    :return:
+    """
+    unit = SwordsDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    assert unit.rare == getattr(Rare, f"Star_{rare}")
+    assert unit.tier == getattr(Tier, f"Tier_{tier}")
+    assert round(unit.main_stat.stat_data) == batk
+    assert unit.seco_stat.stat_name == seco
+    assert unit.type == Sword().type
+    if unit.seco_stat.stat_name != WeaponStatType.none:
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, valu, 1.5)
diff --git a/test/face/__init__.py b/test/face/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/face/info/__init__.py b/test/face/info/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/face/info/test_info.py b/test/face/info/test_info.py
new file mode 100644
index 00000000..f6b0d04a
--- /dev/null
+++ b/test/face/info/test_info.py
@@ -0,0 +1,43 @@
+import pytest
+from PySide6.QtCore import Qt, QUrl
+from PySide6.QtGui import QDesktopServices
+from PySide6.QtWidgets import QDialog
+from pytest_mock.plugin import MockerFixture
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts import __gicompat_part__, __gicompat_vers__, __releases__, __versdata__
+from gi_loadouts.face.wind.main import MainWindow
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.info: Clicking the info button"
+        )
+    ]
+)
+def test_info(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test clicking the info button on side of UI
+
+    :return:
+    """
+
+    """
+    Perform the action of clicking the info button
+    """
+    qtbot.mouseClick(runner.side_info, Qt.LeftButton)
+    mock_open_url = mocker.patch.object(QDesktopServices, 'openUrl')
+    qtbot.mouseClick(runner.infoobjc.updt, Qt.LeftButton)
+    expected_url = QUrl(__releases__)
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.side_info.toolTip() == "Info"
+    assert isinstance(runner.infoobjc, QDialog)
+    assert runner.infoobjc.windowTitle() == f"Loadouts for Genshin Impact v{__versdata__}"
+    assert runner.infoobjc.vers.text() == f"Version v{__versdata__}"
+    assert runner.infoobjc.comp.text() == f"This version is compatible with Genshin Impact {__gicompat_vers__} Phase {__gicompat_part__}"
+    mock_open_url.assert_called_once_with(expected_url)
diff --git a/test/face/lcns/__init__.py b/test/face/lcns/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/face/lcns/test_lcns.py b/test/face/lcns/test_lcns.py
new file mode 100644
index 00000000..9e8b2667
--- /dev/null
+++ b/test/face/lcns/test_lcns.py
@@ -0,0 +1,37 @@
+import pytest
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QDialog
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts import __gicompat_part__, __gicompat_vers__, __versdata__
+from gi_loadouts.face.wind.main import MainWindow
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.lcns: Clicking the help button"
+        )
+    ]
+)
+def test_lcns(runner: MainWindow, qtbot: QtBot, _: None) -> None:
+    """
+    Test clicking the help button on side of UI
+
+    :return:
+    """
+
+    """
+    Perform the action of clicking the help button
+    """
+    qtbot.mouseClick(runner.side_lcns, Qt.LeftButton)
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.side_lcns.toolTip() == "Help"
+    assert isinstance(runner.lcnsobjc, QDialog)
+    assert runner.lcnsobjc.windowTitle() == f"Loadouts for Genshin Impact v{__versdata__}"
+    assert runner.lcnsobjc.vers.text() == f"Version v{__versdata__}"
+    assert runner.lcnsobjc.comp.text() == f"This version is compatible with Genshin Impact {__gicompat_vers__} Phase {__gicompat_part__}"
diff --git a/test/face/otpt/__init__.py b/test/face/otpt/__init__.py
new file mode 100644
index 00000000..9ed23e7d
--- /dev/null
+++ b/test/face/otpt/__init__.py
@@ -0,0 +1,127 @@
+__test_arti__ = {
+    "fwol": {
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "main": "HP",
+        "type": "Emblem of Severed Fate",
+        "name": "Magnificent Tsuba",
+        "stat": {
+            "a": {
+                "name": "Crit Rate",
+                "data": 12.0
+            },
+            "b": {
+                "name": "Crit DMG",
+                "data": 34.0
+            },
+            "c": {
+                "name": "Elemental Mastery",
+                "data": 56.0
+            },
+            "d": {
+                "name": "Energy Recharge",
+                "data": 78.0
+            },
+        }
+    },
+    "pmod": {
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "main": "ATK",
+        "type": "Emblem of Severed Fate",
+        "name": "Sundered Feather",
+        "stat": {
+            "a": {
+                "name": "DEF",
+                "data": 12.0
+            },
+            "b": {
+                "name": "DEF %",
+                "data": 34.0
+            },
+            "c": {
+                "name": "HP",
+                "data": 56.0
+            },
+            "d": {
+                "name": "HP %",
+                "data": 78.0
+            },
+        }
+    },
+    "sdoe": {
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "main": "Elemental Mastery",
+        "type": "Shimenawa's Reminiscence",
+        "name": "Morning Dew's Moment",
+        "stat": {
+            "a": {
+                "name": "ATK",
+                "data": 12.0
+            },
+            "b": {
+                "name": "ATK %",
+                "data": 34.0
+            },
+            "c": {
+                "name": "Crit Rate",
+                "data": 56.0
+            },
+            "d": {
+                "name": "Crit DMG",
+                "data": 78.0
+            },
+        }
+    },
+    "gboe": {
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "main": "Physical DMG Bonus",
+        "type": "Shimenawa's Reminiscence",
+        "name": "Hopeful Heart",
+        "stat": {
+            "a": {
+                "name": "Elemental Mastery",
+                "data": 12.0
+            },
+            "b": {
+                "name": "Energy Recharge",
+                "data": 34.0
+            },
+            "c": {
+                "name": "HP %",
+                "data": 56.0
+            },
+            "d": {
+                "name": "HP",
+                "data": 78.0
+            },
+        }
+    },
+    "ccol": {
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "main": "Healing Bonus",
+        "type": "Noblesse Oblige",
+        "name": "Royal Masque",
+        "stat": {
+            "a": {
+                "name": "ATK",
+                "data": 12.0
+            },
+            "b": {
+                "name": "ATK %",
+                "data": 34.0
+            },
+            "c": {
+                "name": "Crit Rate",
+                "data": 56.0
+            },
+            "d": {
+                "name": "Crit DMG",
+                "data": 78.0
+            },
+        }
+    }
+}
diff --git a/test/face/otpt/test_otpt.py b/test/face/otpt/test_otpt.py
new file mode 100644
index 00000000..7bb60c87
--- /dev/null
+++ b/test/face/otpt/test_otpt.py
@@ -0,0 +1,197 @@
+from random import choice
+
+import pytest
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QMainWindow, QMessageBox
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts import __versdata__
+from gi_loadouts.data.char import __charmaps__
+from gi_loadouts.data.weap import Family
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.char.cons import Cons
+from gi_loadouts.type.levl import Level
+
+from . import __test_arti__
+
+
+@pytest.mark.parametrize(
+    "type, cond",
+    [
+        pytest.param("Bow", "pair", id="face.otpt.rule: Generate final calculation based on character, bow and 2 piece artifact set"),
+        pytest.param("Catalyst", "pair", id="face.otpt.rule: Generate final calculation based on character, catalyst and 2 piece artifact set"),
+        pytest.param("Claymore", "pair", id="face.otpt.rule: Generate final calculation based on character, claymore and 2 piece artifact set"),
+        pytest.param("Polearm", "pair", id="face.otpt.rule: Generate final calculation based on character, polearm and 2 piece artifact set"),
+        pytest.param("Sword", "pair", id="face.otpt.rule: Generate final calculation based on character, sword and 2 piece artifact set"),
+        pytest.param("Bow", "quad", id="face.otpt.rule: Generate final calculation based on character, bow and 4 piece artifact set"),
+        pytest.param("Catalyst", "quad", id="face.otpt.rule: Generate final calculation based on character, catalyst and 4 piece artifact set"),
+        pytest.param("Claymore", "quad", id="face.otpt.rule: Generate final calculation based on character, claymore and 4 piece artifact set"),
+        pytest.param("Polearm", "quad", id="face.otpt.rule: Generate final calculation based on character, polearm and 4 piece artifact set"),
+        pytest.param("Sword", "quad", id="face.otpt.rule: Generate final calculation based on character, sword and 4 piece artifact set"),
+        pytest.param("Bow", "unique", id="face.otpt.rule: Generate final calculation based on character, bow and unique artifacts"),
+        pytest.param("Catalyst", "unique", id="face.otpt.rule: Generate final calculation based on character, catalyst and unique artifacts"),
+        pytest.param("Claymore", "unique", id="face.otpt.rule: Generate final calculation based on character, claymore and unique artifacts"),
+        pytest.param("Polearm", "unique", id="face.otpt.rule: Generate final calculation based on character, polearm and unique artifacts"),
+        pytest.param("Sword", "unique", id="face.otpt.rule: Generate final calculation based on character, sword and unique artifacts"),
+        pytest.param("Bow", "none", id="face.otpt.rule: Generate final calculation based on character, bow and no artifacts"),
+        pytest.param("Catalyst", "none", id="face.otpt.rule: Generate final calculation based on character, catalyst and no artifacts"),
+        pytest.param("Claymore", "none", id="face.otpt.rule: Generate final calculation based on character, claymore and no artifacts"),
+        pytest.param("Polearm", "none", id="face.otpt.rule: Generate final calculation based on character, polearm and no artifacts"),
+        pytest.param("Sword", "none", id="face.otpt.rule: Generate final calculation based on character, sword and no artifacts"),
+    ]
+)
+def test_otpt(runner: MainWindow, qtbot: QtBot, type: str, cond: str) -> None:
+    """
+    Test generating final calculation based on character, weapon and artifacts
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    if cond == "quad":
+        """
+        Modify variable __test_arti__ to store 4 piece artifact set
+        """
+        __test_arti__["sdoe"]["type"] = "Emblem of Severed Fate"
+        __test_arti__["sdoe"]["name"] = "Storm Cage"
+        __test_arti__["ccol"]["type"] = "Emblem of Severed Fate"
+        __test_arti__["ccol"]["name"] = "Ornate Kabuto"
+    elif cond == "unique":
+        """
+        Modify variable __test_arti__ to store unique artifacts
+        """
+        __test_arti__["pmod"]["type"] = "Marechaussee Hunter"
+        __test_arti__["pmod"]["name"] = "Masterpiece's Overture"
+        __test_arti__["gboe"]["type"] = "Retracing Bolide"
+        __test_arti__["gboe"]["name"] = "Summer Night's Waterballoon"
+    elif cond == "none":
+        """
+        Modify variable __test_arti__ to store no artifacts
+        """
+        __test_arti__["fwol"]["type"] = "None"
+        __test_arti__["pmod"]["type"] = "None"
+        __test_arti__["sdoe"]["type"] = "None"
+        __test_arti__["gboe"]["type"] = "None"
+        __test_arti__["ccol"]["type"] = "None"
+
+    """
+    Setting the character statistics
+    """
+    c_name = choice([name for name, data in __charmaps__.items() if data().weapon.value == type])
+    c_levl = choice(list(item.value.name for item in Level))
+    c_cons = choice(list(item.value.name for item in Cons))
+    runner.head_char_name.setCurrentText(c_name)
+    runner.head_char_levl.setCurrentText(c_levl)
+    runner.head_char_cons.setCurrentText(c_cons)
+
+    """
+    Setting the weapon statistics
+    """
+    w_name = choice(list(Family[type].keys()))
+    w_objc = Family[type][w_name]()
+    w_levl = choice(w_objc.levl_bind).value.name
+    w_refn = f"({choice(list(w_objc.refinement.keys()))})" if w_objc.refinement.keys() else ""
+    runner.weap_area_name.setCurrentText(w_name)
+    runner.weap_area_levl.setCurrentText(w_levl)
+    runner.weap_area_refn.setCurrentText(w_refn.replace("(","").replace(")","")) if w_refn != "" else None
+
+    for area in ["fwol", "pmod", "sdoe", "gboe", "ccol"]:
+        getattr(runner, f"arti_{area}_type").setCurrentText(__test_arti__[area]["type"])
+
+    if cond != "none":
+        for area in ["fwol", "pmod", "sdoe", "gboe", "ccol"]:
+            getattr(runner, f"arti_{area}_rare").setCurrentText(__test_arti__[area]["rare"])
+            getattr(runner, f"arti_{area}_levl").setCurrentText(__test_arti__[area]["levl"])
+            getattr(runner, f"arti_{area}_name_main").setCurrentText(__test_arti__[area]["main"])
+            for alfa in ["a", "b", "c", "d"]:
+                getattr(runner, f"arti_{area}_name_{alfa}").setCurrentText(__test_arti__[area]["stat"][alfa]["name"])
+                getattr(runner, f"arti_{area}_data_{alfa}").setText(str(__test_arti__[area]["stat"][alfa]["data"]))
+
+    if runner.collection.quad != "":
+        artiline = f"4 x <b>{runner.collection.quad}</b>"
+    elif len(runner.collection.pair) >= 1:
+        artiline = ", ".join([f"2 x <b>{item}</b>" for item in runner.collection.pair])
+    else:
+        artiline = "No artifact set bonus."
+
+    """
+    Perform the action of clicking the help button
+    """
+    qtbot.mouseClick(runner.head_scan, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.head_scan.toolTip() == "Generate"
+    assert isinstance(runner.otptobjc, QMainWindow)
+    assert runner.otptobjc.windowTitle() == f"Loadouts for Genshin Impact v{__versdata__} - {runner.head_char_name.currentText()}"
+    assert runner.otptobjc.windowModality() == Qt.ApplicationModal
+    assert runner.otptobjc.head_area_line_prim.text() == f"<b>{c_name}</b> - {c_levl} ({c_cons})"
+    assert runner.otptobjc.head_area_line_seco.text() == f"<b>{w_name}</b> - {w_levl} {w_refn}" + "<br/>" + f"{artiline}"
+    assert runner.otptobjc.area_hlpt_data.text() == str(round(runner.otptobjc.tyvt.health_points.stat_data, 1))
+    assert runner.otptobjc.area_hlpt_calc.text() == f"{round(runner.otptobjc.tyvt.addendum_base_health_points.stat_data, 1)} + {round(runner.otptobjc.tyvt.addendum_plus_health_points.stat_data, 1)}"
+    assert runner.otptobjc.area_attk_data.text() == str(round(runner.otptobjc.tyvt.attack.stat_data, 1))
+    assert runner.otptobjc.area_attk_calc.text() == f"{round(runner.otptobjc.tyvt.addendum_base_attack.stat_data, 1)} + {round(runner.otptobjc.tyvt.addendum_plus_attack.stat_data, 1)}"
+    assert runner.otptobjc.area_dfns_data.text() == str(round(runner.otptobjc.tyvt.defense.stat_data, 1))
+    assert runner.otptobjc.area_dfns_calc.text() == f"{round(runner.otptobjc.tyvt.addendum_base_defense.stat_data, 1)} + {round(runner.otptobjc.tyvt.addendum_plus_defense.stat_data, 1)}"
+    assert runner.otptobjc.area_elma_data.text() == str(round(runner.otptobjc.tyvt.elemental_mastery.stat_data, 1))
+    assert runner.otptobjc.area_crit_rate_data.text() == f"{str(round(runner.otptobjc.tyvt.critical_rate_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_crit_dmge_data.text() == f"{str(round(runner.otptobjc.tyvt.critical_damage_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_heal_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.healing_bonus_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_heal_incm_data.text() == f"{str(round(runner.otptobjc.tyvt.incoming_healing_bonus_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_enrc_data.text() == f"{str(round(runner.otptobjc.tyvt.energy_recharge_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_cldn_data.text() == f"{str(round(runner.otptobjc.tyvt.cooldown_reduction_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_sdsh_data.text() == f"{str(round(runner.otptobjc.tyvt.shield_strength_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_pyro_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.damage_bonus_pyro_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_pyro_resi_data.text() == f"{str(round(runner.otptobjc.tyvt.resistance_pyro_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_hydo_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.damage_bonus_hydro_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_hydo_resi_data.text() == f"{str(round(runner.otptobjc.tyvt.resistance_hydro_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_dend_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.damage_bonus_dendro_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_dend_resi_data.text() == f"{str(round(runner.otptobjc.tyvt.resistance_dendro_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_elec_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.damage_bonus_electro_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_elec_resi_data.text() == f"{str(round(runner.otptobjc.tyvt.resistance_electro_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_anem_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.damage_bonus_anemo_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_anem_resi_data.text() == f"{str(round(runner.otptobjc.tyvt.resistance_anemo_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_cryo_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.damage_bonus_cryo_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_cryo_resi_data.text() == f"{str(round(runner.otptobjc.tyvt.resistance_cryo_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_geox_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.damage_bonus_geo_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_geox_resi_data.text() == f"{str(round(runner.otptobjc.tyvt.resistance_geo_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_phys_perc_data.text() == f"{str(round(runner.otptobjc.tyvt.damage_bonus_physical_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_phys_resi_data.text() == f"{str(round(runner.otptobjc.tyvt.resistance_physical_perc.stat_data, 1))}%"
+    assert runner.otptobjc.area_crvl_data.text() == f"{str(round(runner.otptobjc.tyvt.critical_damage_perc.stat_data + 2*(runner.otptobjc.tyvt.critical_rate_perc.stat_data), 1))}"
+
+
+@pytest.mark.parametrize(
+    "subdata",
+    [
+        pytest.param("empty", id="face.otpt.rule: Failing to generate final calculation based on character, bow and default artifact set with empty substats value"),
+        pytest.param("invalid", id="face.otpt.rule: Failing to generate final calculation based on character, bow and default artifact set with invalid substats value")
+    ]
+)
+def test_otpt_fail(runner: MainWindow, qtbot: QtBot, subdata: str) -> None:
+    """
+    Test failing to generate final calculation based on character, weapon and default artifact
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    if subdata == "invalid":
+        runner.arti_fwol_data_a.setText("abc")
+
+    """
+    Perform the action of clicking the help button
+    """
+    qtbot.mouseClick(runner.head_scan, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Inaccuracy detected"
+    assert "Please consider checking your input." in runner.dialog.text()
diff --git a/test/face/scan/__init__.py b/test/face/scan/__init__.py
new file mode 100644
index 00000000..74cd5bf4
--- /dev/null
+++ b/test/face/scan/__init__.py
@@ -0,0 +1,158 @@
+from PySide6.QtWidgets import QDialog
+
+from gi_loadouts.face.util import modify_datatype_to_transfer
+from gi_loadouts.type.arti.base import (
+    MainStatType_CCOL,
+    MainStatType_FWOL,
+    MainStatType_GBOE,
+    MainStatType_PMOD,
+    MainStatType_SDOE,
+)
+from gi_loadouts.type.stat import ATTR, STAT
+
+__dist__ = {
+    "Flower of Life": {"list": MainStatType_FWOL, "part": "fwol"},
+    "Plume of Death": {"list": MainStatType_PMOD, "part": "pmod"},
+    "Sands of Eon": {"list": MainStatType_SDOE, "part": "sdoe"},
+    "Goblet of Eonothem": {"list": MainStatType_GBOE, "part": "gboe"},
+    "Circlet of Logos": {"list": MainStatType_CCOL, "part": "ccol"},
+}
+
+__rtrn__ = {
+    "Flower of Life": {
+        "part": "fwol",
+        "team": "Nighttime Whispers in the Echoing Woods",
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "stat": {
+            "main": ATTR(stat_name=STAT.health_points, stat_data=4780.0),
+            "seco": {
+                "a": ATTR(stat_name=STAT.critical_rate_perc, stat_data=6.2),
+                "b": ATTR(stat_name=STAT.critical_damage_perc, stat_data=14.0),
+                "c": ATTR(stat_name=STAT.defense_perc, stat_data=12.4),
+                "d": ATTR(stat_name=STAT.defense, stat_data=53.0)
+            }
+        }
+    },
+    "Plume of Death": {
+        "part": "pmod",
+        "team": "Shimenawa's Reminiscence",
+        "rare": "Star 5",
+        "levl": "Level 18",
+        "stat": {
+            "main": ATTR(stat_name=STAT.attack, stat_data=285.0),
+            "seco": {
+                "a": ATTR(stat_name=STAT.critical_rate_perc, stat_data=3.1),
+                "b": ATTR(stat_name=STAT.critical_damage_perc, stat_data=21.8),
+                "c": ATTR(stat_name=STAT.attack_perc, stat_data=21.0),
+                "d": ATTR(stat_name=STAT.defense, stat_data=21.0)
+            }
+        }
+    },
+    "Sands of Eon": {
+        "part": "sdoe",
+        "team": "Shimenawa's Reminiscence",
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "stat": {
+            "main": ATTR(stat_name=STAT.energy_recharge_perc, stat_data=51.8),
+            "seco": {
+                "a": ATTR(stat_name=STAT.elemental_mastery, stat_data=23.0),
+                "b": ATTR(stat_name=STAT.critical_rate_perc, stat_data=6.6),
+                "c": ATTR(stat_name=STAT.critical_damage_perc, stat_data=21.0),
+                "d": ATTR(stat_name=STAT.health_points_perc, stat_data=13.4)
+            }
+        }
+    },
+    "Goblet of Eonothem": {
+        "part": "gboe",
+        "team": "Nighttime Whispers in the Echoing Woods",
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "stat": {
+            "main": ATTR(stat_name=STAT.damage_bonus_geo_perc, stat_data=46.6),
+            "seco": {
+                "a": ATTR(stat_name=STAT.critical_damage_perc, stat_data=5.4),
+                "b": ATTR(stat_name=STAT.health_points, stat_data=568.0),
+                "c": ATTR(stat_name=STAT.defense, stat_data=56.0),
+                "d": ATTR(stat_name=STAT.attack, stat_data=35.0)
+            }
+        }
+    },
+    "Circlet of Logos": {
+        "part": "ccol",
+        "team": "Crimson Witch of Flames",
+        "rare": "Star 5",
+        "levl": "Level 20",
+        "stat": {
+            "main": ATTR(stat_name=STAT.critical_rate_perc, stat_data=31.1),
+            "seco": {
+                "a": ATTR(stat_name=STAT.critical_damage_perc, stat_data=13.2),
+                "b": ATTR(stat_name=STAT.health_points_perc, stat_data=5.8),
+                "c": ATTR(stat_name=STAT.health_points, stat_data=717.0),
+                "d": ATTR(stat_name=STAT.attack, stat_data=53.0)
+            }
+        }
+    }
+}
+
+__rtrn_flty__ = {
+    "part": "sdoe",
+    "team": "Shimenawa's Reminiscence",
+    "rare": "Star 5",
+    "levl": "Level 20",
+    "stat": {
+        "main": ATTR(stat_name=STAT.energy_recharge_perc, stat_data=51.8),
+        "seco": {
+            "a": ATTR(stat_name=STAT.elemental_mastery, stat_data=23.0),
+            "b": ATTR(stat_name=STAT.critical_rate_perc, stat_data=modify_datatype_to_transfer(" ")),
+            "c": ATTR(stat_name=STAT.critical_damage_perc, stat_data=21.0),
+            "d": ATTR(stat_name=STAT.health_points_perc, stat_data=modify_datatype_to_transfer("abc"))
+        }
+    }
+}
+
+
+class MockScanDialog:
+    def exec(self):
+        return QDialog.Accepted
+
+
+class MockScanDialogFWOL(MockScanDialog):
+    def __init__(self, part: str = "fwol") -> None:
+        self.part = part
+
+    def keep_info(self) -> dict:
+        return __rtrn__["Flower of Life"]
+
+
+class MockScanDialogPMOD(MockScanDialog):
+    def __init__(self, part: str = "pmod") -> None:
+        self.part = part
+
+    def keep_info(self) -> dict:
+        return __rtrn__["Plume of Death"]
+
+
+class MockScanDialogSDOE(MockScanDialog):
+    def __init__(self, part: str = "sdoe") -> None:
+        self.part = part
+
+    def keep_info(self) -> dict:
+        return __rtrn__["Sands of Eon"]
+
+
+class MockScanDialogGBOE(MockScanDialog):
+    def __init__(self, part: str = "gboe") -> None:
+        self.part = part
+
+    def keep_info(self) -> dict:
+        return __rtrn__["Goblet of Eonothem"]
+
+
+class MockScanDialogCCOL(MockScanDialog):
+    def __init__(self, part: str = "ccol") -> None:
+        self.part = part
+
+    def keep_info(self) -> dict:
+        return __rtrn__["Circlet of Logos"]
diff --git a/test/face/scan/test_scan.py b/test/face/scan/test_scan.py
new file mode 100644
index 00000000..d8f75f1e
--- /dev/null
+++ b/test/face/scan/test_scan.py
@@ -0,0 +1,592 @@
+from os import remove
+from random import choice, randbytes
+from tempfile import NamedTemporaryFile
+from uuid import uuid4
+
+import pytest
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox
+from pytesseract import TesseractError
+from pytest_mock import MockerFixture
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts import __versdata__, conf
+from gi_loadouts.data.arti import __artilist__
+from gi_loadouts.face.rsrc import kill_temp_file, make_temp_file
+from gi_loadouts.face.scan import file
+from gi_loadouts.face.scan.main import ScanDialog
+from gi_loadouts.face.util import truncate_text
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.arti import ArtiLevl, base
+from gi_loadouts.type.stat import STAT
+from test.face.scan import (
+    MockScanDialogCCOL,
+    MockScanDialogFWOL,
+    MockScanDialogGBOE,
+    MockScanDialogPMOD,
+    MockScanDialogSDOE,
+    __dist__,
+    __rtrn__,
+    __rtrn_flty__,
+)
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.scan: Testing ScanWindow")
+    ]
+)
+def test_scan_window(scantest: ScanDialog, _: None) -> None:
+    """
+    Test spawning of an instance of ScanWindow class
+
+    :return:
+    """
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(scantest, QDialog)
+    assert scantest.windowTitle() == f"Loadouts for Genshin Impact v{__versdata__}"
+
+
+@pytest.mark.parametrize(
+    "name, rare, part, part_type",
+    [
+        pytest.param(
+            name, team.rare, team.fwol, "Flower of Life", id=f"face.scan.rule: Configuring Flower of Life artifact - {name}"
+        ) for name, team in __artilist__.items()
+    ] +
+    [
+        pytest.param(
+            name, team.rare, team.pmod, "Plume of Death", id=f"face.scan.rule: Configuring Plume of Death artifact - {name}"
+        ) for name, team in __artilist__.items()
+    ] +
+    [
+        pytest.param(
+            name, team.rare, team.sdoe, "Sands of Eon", id=f"face.scan.rule: Configuring Sands of Eon artifact - {name}"
+        ) for name, team in __artilist__.items()
+    ] +
+    [
+        pytest.param(
+            name, team.rare, team.gboe, "Goblet of Eonothem", id=f"face.scan.rule: Configuring Goblet of Eonothem artifact - {name}"
+        ) for name, team in __artilist__.items()
+    ] +
+    [
+        pytest.param(
+            name, team.rare, team.ccol, "Circlet of Logos", id=f"face.scan.rule: Configuring Circlet of Logos artifact - {name}"
+        ) for name, team in __artilist__.items()
+    ]
+)
+def test_scan_arti_drop(scantest: ScanDialog, name: str, rare: list, part: str, part_type: str) -> None:
+    """
+    Test configuring artifact on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    conf = dict()
+
+    conf["dist"] = part_type
+    scantest.arti_dist.setCurrentText(conf["dist"])
+    conf["name"] = name
+    scantest.arti_type.setCurrentText(conf["name"])
+    conf["rare"] = choice([item for item in rare])
+    scantest.arti_rare.setCurrentText(conf["rare"].value.name)
+    conf["levl"] = choice([item for item in ArtiLevl if conf["rare"] in item.value.rare])
+    scantest.arti_levl.setCurrentText(conf["levl"].value.name)
+    conf["stat"] = choice([item for item in getattr(base, __dist__[conf["dist"]]["list"].__name__) if item.value != STAT.none])
+    scantest.arti_name_main.setCurrentText(conf["stat"].value.value)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    if name == "None":
+        return
+
+    assert scantest.arti_type_name.text() == truncate_text(part.__name__, 34)
+    assert scantest.arti_data_main.text() == str(round(part.stat_data, 1))
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.scan.rule: Clearing artifact")
+    ]
+)
+def test_scan_arti_wipe(scantest: ScanDialog, qtbot: QtBot, _: None) -> None:
+    """
+    Test clearing of the artifact from the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    qtbot.mouseClick(scantest.arti_back_wipe, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert scantest.arti_type.currentText() == "None"
+    assert scantest.arti_levl.currentText() == "None"
+    assert scantest.arti_rare.currentText() == "Star 0"
+    assert scantest.arti_type_name.text() == "None"
+    for item in ["main", "a", "b", "c", "d"]:
+        assert getattr(scantest, f"arti_name_{item}").currentText() == "None"
+        assert getattr(scantest, f"arti_data_{item}").text() == ""
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.scan.rule: Test OCR functionality")
+    ]
+)
+def test_scan_arti_load(scantest: ScanDialog, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test initial OCR functionality
+
+    :return:
+    """
+
+    """
+    Create the Tesseract training data
+    """
+    mocker.patch("gi_loadouts.conf.data_prefix", f"{uuid4().hex.upper()[0:8]}-")
+    make_temp_file()
+
+    """
+    Perform the action of loading the artifact information
+    """
+    tempfile = "test/static/gi-loadouts-ocr-test-sdoe.png"
+    savefile = NamedTemporaryFile(prefix="gi-loadouts-ocr-test-sdoe-", suffix=".png", delete=False, mode="wb")
+    with open(tempfile, "rb") as src_file:
+        savefile.write(src_file.read())
+    savefile.close()
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(savefile.name, ""))
+    qtbot.mouseClick(scantest.arti_cnvs_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    def check_label():
+        """
+        Checking the interface elements change asynchronously
+        """
+        assert scantest.arti_text.text() == "Browse your local storage to load a high quality screenshot of your artifact and the statistics will automatically be computed from there."
+        assert scantest.arti_type.currentText() == "Shimenawa's Reminiscence"
+        assert scantest.arti_levl.currentText() == "Level 20"
+        assert scantest.arti_rare.currentText() == "Star 5"
+        assert scantest.arti_type_name.text() == "Morning Dew's Moment"
+        assert scantest.arti_name_main.currentText() == "Energy Recharge"
+        assert scantest.arti_data_main.text() == "51.8"
+        assert scantest.arti_name_a.currentText() == "Elemental Mastery"
+        assert scantest.arti_data_a.text() == "23.0"
+        assert scantest.arti_name_b.currentText() == "Crit Rate"
+        assert scantest.arti_data_b.text() == "6.6"
+        assert scantest.arti_name_c.currentText() == "Crit DMG"
+        assert scantest.arti_data_c.text() == "21.0"
+        assert scantest.arti_name_d.currentText() == "HP %"
+        assert scantest.arti_data_d.text() == "13.4"
+
+    qtbot.waitUntil(check_label, timeout=10000)
+
+    """
+    Cleanup the temporary files from temp directory
+    """
+    kill_temp_file()
+    remove(savefile.name)
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.scan.rule: Cancelling loading an artifact snapshot")
+    ]
+)
+def test_scan_arti_load_nope(scantest: ScanDialog, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test cancelling loading an artifact snapshot
+
+    :return:
+    """
+
+    """
+    Store the artifact information before trying to load
+    """
+    init_arti_type = scantest.arti_type.currentText()
+    init_arti_levl = scantest.arti_levl.currentText()
+    init_arti_rare = scantest.arti_rare.currentText()
+    init_arti_type_name = scantest.arti_type_name.text()
+    init_arti_name_main = scantest.arti_name_main.currentText()
+    init_arti_data_main = scantest.arti_data_main.text()
+    init_arti_name_a = scantest.arti_name_a.currentText()
+    init_arti_data_a = scantest.arti_data_a.text()
+    init_arti_name_b = scantest.arti_name_b.currentText()
+    init_arti_data_b = scantest.arti_data_b.text()
+    init_arti_name_c = scantest.arti_name_c.currentText()
+    init_arti_data_c = scantest.arti_data_c.text()
+    init_arti_name_d = scantest.arti_name_d.currentText()
+    init_arti_data_d = scantest.arti_data_d.text()
+
+    """
+    Perform the action of loading the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load_screenshot", return_value=(False, None, None))
+    qtbot.mouseClick(scantest.arti_cnvs_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements stayed the same
+    """
+    assert scantest.arti_type.currentText() == init_arti_type
+    assert scantest.arti_levl.currentText() == init_arti_levl
+    assert scantest.arti_rare.currentText() == init_arti_rare
+    assert scantest.arti_type_name.text() == init_arti_type_name
+    assert scantest.arti_name_main.currentText() == init_arti_name_main
+    assert scantest.arti_data_main.text() == init_arti_data_main
+    assert scantest.arti_name_a.currentText() == init_arti_name_a
+    assert scantest.arti_data_a.text() == init_arti_data_a
+    assert scantest.arti_name_b.currentText() == init_arti_name_b
+    assert scantest.arti_data_b.text() == init_arti_data_b
+    assert scantest.arti_name_c.currentText() == init_arti_name_c
+    assert scantest.arti_data_c.text() == init_arti_data_c
+    assert scantest.arti_name_d.currentText() == init_arti_name_d
+    assert scantest.arti_data_d.text() == init_arti_data_d
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.scan.rule: Failing to load the artifact snapshot")
+    ]
+)
+def test_scan_arti_load_fail(scantest: ScanDialog, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test failing to loading the artifact snapshot
+
+    :return:
+    """
+
+    """
+    Create a temporary file filled with random data to simulate a failure when trying to open an
+    image using PIL.Image.open().
+    """
+    savefile = NamedTemporaryFile(prefix="gi-loadouts-", suffix=".webp", delete=False, mode="wb")
+    savefile.write(randbytes(512*1024))
+    savefile.close()
+
+    """
+    Perform the action of loading the artifact snapshot
+    """
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(savefile.name, ""))
+    qtbot.mouseClick(scantest.arti_cnvs_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(scantest.dialog, QMessageBox)
+    assert scantest.dialog.icon() == QMessageBox.Information
+    assert scantest.dialog.windowTitle() == "Faulty scanning"
+    assert "Please select an accurate screenshot." in scantest.dialog.text()
+    assert scantest.dialog.isVisible()
+
+    """
+    Cleanup the temporary files from temp directory
+    """
+    kill_temp_file()
+    remove(savefile.name)
+
+
+@pytest.mark.parametrize(
+    "platform, tempexec",
+    [
+        pytest.param("Linux", "/usr/bin/tesseract", id="face.scan.rule: Actual loading of Tesseract OCR executable in Linux"),
+        pytest.param("Windows", "C:\\Program Files\\Tesseract-OCR\\tesseract.exe", id="face.scan.rule: Actual loading of Tesseract OCR executable in Windows")
+    ]
+)
+def test_scan_tessexec_load(scantest: ScanDialog, qtbot: QtBot, mocker: MockerFixture, platform: str, tempexec: str) -> None:
+    """
+    Test actual loading of Tesseract OCR executable
+
+    :return:
+    """
+
+    """
+    Store the initial conf.tessexec so that after this test it can be restored to the original value
+    """
+    temp = conf.tessexec
+
+    """
+    Mock the system environment and reload the tessexec variable to apply the mocked path
+    """
+    mocker.patch("gi_loadouts.conf.system", return_value=platform)
+    conf.tessexec = conf.get_tessexec_path()
+
+    """
+    Perform the action of loading the actual Tesseract OCR executable
+    """
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(tempexec, ""))
+    qtbot.mouseClick(scantest.arti_cnvs_conf, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert conf.tessexec == tempexec
+
+    """
+    Reinstate the path for Tesseract OCR executable
+    """
+    conf.tessexec = temp
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.scan.rule: Failing to load the Tesseract OCR executable")
+    ]
+)
+def test_scan_tessexec_load_fail(scantest: ScanDialog, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test failing to load the Tesseract OCR executable
+
+    :return:
+    """
+
+    """
+    Perform the action of loading the Tesseract OCR executable
+    """
+    mocker.patch.object(file.FileHandling, "load_tessexec", side_effect=Exception)
+    qtbot.mouseClick(scantest.arti_cnvs_conf, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(scantest.dialog, QMessageBox)
+    assert scantest.dialog.icon() == QMessageBox.Information
+    assert scantest.dialog.windowTitle() == "Faulty scanning"
+    assert "Please consider checking your input after ensuring that the proper Tesseract OCR executable has been selected." in scantest.dialog.text()
+    assert scantest.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "expt",
+    [
+        pytest.param(OSError, id="face.scan.rule: Failing of register function with OSError"),
+        pytest.param(TesseractError("abc", "xyz"), id="face.scan.rule: Failing of register function with TesseractError")
+    ]
+)
+def test_scan_register_fail(scantest: ScanDialog, qtbot: QtBot, mocker: MockerFixture, expt: Exception) -> None:
+    """
+    Test failing the register function with `OSError` and `TesseractError`
+
+    :return:
+    """
+
+    """
+    Create the tesseract training data
+    """
+    mocker.patch("gi_loadouts.conf.data_prefix", f"{uuid4().hex.upper()[0:8]}-")
+    make_temp_file()
+
+    """
+    Perform the action of loading the artifact information
+    """
+    tempfile = "test/static/gi-loadouts-ocr-test-sdoe.png"
+    savefile = NamedTemporaryFile(prefix="gi-loadouts-ocr-test-sdoe-", suffix=".png", delete=False, mode="wb")
+    with open(tempfile, "rb") as src_file:
+        savefile.write(src_file.read())
+    savefile.close()
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(savefile.name, ""))
+    mocker.patch("gi_loadouts.face.scan.work.image_to_string", side_effect=expt)
+    qtbot.mouseClick(scantest.arti_cnvs_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    def check_label():
+        """
+        Checking the interface elements change asynchronously
+        """
+        assert isinstance(scantest.dialog, QMessageBox)
+        assert scantest.dialog.icon() == QMessageBox.Information
+        assert scantest.dialog.windowTitle() == "Faulty scanning"
+        if isinstance(expt, OSError):
+            assert "Selected executable of Tesseract OCR is unfunctional." in scantest.dialog.text()
+        elif isinstance(expt, TesseractError):
+            assert "Processing failed as either Tesseract OCR executable ceased to function or training data was tampered with." in scantest.dialog.text()
+        assert scantest.dialog.isVisible()
+
+    qtbot.waitUntil(check_label, timeout=10000)
+
+    """
+    Cleanup the temporary files from temp directory
+    """
+    kill_temp_file()
+    remove(savefile.name)
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.scan.rule: Clearing snapshot")
+    ]
+)
+def test_scan_snapshot_wipe(scantest: ScanDialog, qtbot: QtBot, _: None) -> None:
+    """
+    Test clearing of the snapshot from the user interface
+
+    :return:
+    """
+
+    """
+    Perform the action to clear the snapshot
+    """
+    qtbot.mouseClick(scantest.arti_cnvs_wipe, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert scantest.arti_shot.text() == "YOUR ARTIFACT SCREENSHOT WILL SHOW UP HERE"
+
+
+@pytest.mark.parametrize(
+    "dist, part",
+    [
+        pytest.param("Flower of Life", "fwol", id="face.scan.rule: Importing Flower of Life artifact information in MainWindow"),
+        pytest.param("Plume of Death", "pmod", id="face.scan.rule: Importing Plume of Death artifact information in MainWindow"),
+        pytest.param("Sands of Eon", "sdoe", id="face.scan.rule: Importing Sands of Eon artifact information in MainWindow"),
+        pytest.param("Goblet of Eonothem", "gboe", id="face.scan.rule: Importing Goblet of Eonothem artifact information in MainWindow"),
+        pytest.param("Circlet of Logos", "ccol", id="face.scan.rule: Importing Circlet of Logos artifact information in MainWindow")
+    ]
+)
+def test_scan_import_arti(scantest: ScanDialog, qtbot: QtBot, mocker: MockerFixture, dist: str, part: str) -> None:
+    """
+    Test importing artifact information in MainWindow
+
+    :return:
+    """
+
+    """
+    Create the Tesseract training data
+    """
+    mocker.patch("gi_loadouts.conf.data_prefix", f"{uuid4().hex.upper()[0:8]}-")
+    make_temp_file()
+
+    """
+    Perform the action of loading the artifact information
+    """
+    tempfile = f"test/static/gi-loadouts-ocr-test-{part}.png"
+    savefile = NamedTemporaryFile(prefix=f"gi-loadouts-ocr-test-{part}", suffix=".png", delete=False, mode="wb")
+    with open(tempfile, "rb") as src_file:
+        savefile.write(src_file.read())
+    savefile.close()
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(savefile.name, ""))
+    qtbot.mouseClick(scantest.arti_cnvs_load, Qt.LeftButton)
+
+    init = scantest.arti_type.currentText()
+    qtbot.waitUntil(lambda: scantest.arti_type.currentText() != init, timeout=10000)
+
+    """
+    Perform the action of importing artifact info in MainWindow
+    """
+    qtbot.mouseClick(scantest.arti_back_done, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert scantest.keep_info() == __rtrn__[dist]
+
+    """
+    Cleanup the temporary files from temp directory
+    """
+    kill_temp_file()
+    remove(savefile.name)
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.scan.rule: Importing faulty artifact information in MainWindow")
+    ]
+)
+def test_scan_import_arti_flty(scantest: ScanDialog, qtbot: QtBot, _: None) -> None:
+    """
+    Test importing faulty artifact information in MainWindow
+
+    :return:
+    """
+
+    """
+    Set values in scan interface before before transferring it to MainWindow
+    """
+    scantest.arti_dist.setCurrentText("Sands of Eon")
+    scantest.arti_type.setCurrentText("Shimenawa's Reminiscence")
+    scantest.arti_rare.setCurrentText("Star 5")
+    scantest.arti_levl.setCurrentText("Level 20")
+    scantest.arti_name_main.setCurrentText("Energy Recharge")
+    scantest.arti_name_a.setCurrentText("Elemental Mastery")
+    scantest.arti_data_a.setText("23.0")
+    scantest.arti_name_b.setCurrentText("Crit Rate")
+    scantest.arti_data_b.setText("")
+    scantest.arti_name_c.setCurrentText("Crit DMG")
+    scantest.arti_data_c.setText("21.0")
+    scantest.arti_name_d.setCurrentText("HP %")
+    scantest.arti_data_d.setText("abc")
+
+    """
+    Perform the action of importing artifact info in MainWindow
+    """
+    qtbot.mouseClick(scantest.arti_back_done, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert scantest.keep_info() == __rtrn_flty__
+
+
+@pytest.mark.parametrize(
+    "dist, part, mockscan",
+    [
+        pytest.param("Flower of Life", "fwol", MockScanDialogFWOL, id="face.scan.rule: Importing Flower of Life artifact information in MainWindow"),
+        pytest.param("Plume of Death", "pmod", MockScanDialogPMOD, id="face.scan.rule: Importing Plume of Death artifact information in MainWindow"),
+        pytest.param("Sands of Eon", "sdoe", MockScanDialogSDOE, id="face.scan.rule: Importing Sands of Eon artifact information in MainWindow"),
+        pytest.param("Goblet of Eonothem", "gboe", MockScanDialogGBOE, id="face.scan.rule: Importing Goblet of Eonothem artifact information in MainWindow"),
+        pytest.param("Circlet of Logos", "ccol", MockScanDialogCCOL, id="face.scan.rule: Importing Circlet of Logos artifact information in MainWindow")
+    ]
+)
+def test_wind_import_arti(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, dist: str, part: str, mockscan: str) -> None:
+    """
+    Test actually importing artifact information in MainWindow
+
+    :return:
+    """
+
+    """
+    Mock the ScanDialog class to ensure predictable outcome
+    """
+    mocker.patch("gi_loadouts.face.wind.rule.ScanDialog", mockscan)
+
+    """
+    Perform the action of importing artifact info in MainWindow
+    """
+    qtbot.mouseClick(getattr(runner, f"arti_{part}_scan"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.scanobjc.exec() == QDialog.Accepted
+    assert isinstance(runner.scanobjc.keep_info(), dict)
+    assert getattr(runner, f"arti_{part}_type").currentText() == __rtrn__[dist]["team"]
+    assert getattr(runner, f"arti_{part}_rare").currentText() == __rtrn__[dist]["rare"]
+    assert getattr(runner, f"arti_{part}_levl").currentText() == __rtrn__[dist]["levl"]
+    assert getattr(runner, f"arti_{part}_name_main").currentText() == __rtrn__[dist]["stat"]["main"].stat_name
+    assert getattr(runner, f"arti_{part}_data_main").text() == str(__rtrn__[dist]["stat"]["main"].stat_data)
+    for sbst in ["a", "b", "c", "d"]:
+        assert getattr(runner, f"arti_{part}_name_{sbst}").currentText() == __rtrn__[dist]["stat"]["seco"][f"{sbst}"].stat_name
+        assert getattr(runner, f"arti_{part}_data_{sbst}").text() == str(__rtrn__[dist]["stat"]["seco"][f"{sbst}"].stat_data)
diff --git a/test/face/wind/__init__.py b/test/face/wind/__init__.py
new file mode 100644
index 00000000..9510a27e
--- /dev/null
+++ b/test/face/wind/__init__.py
@@ -0,0 +1,6 @@
+class MockQFile:
+    def __init__(self, file_path: str) -> None:
+        self.file_path = file_path
+
+    def open(self, method) -> bool:
+        return False
diff --git a/test/face/wind/arti/__init__.py b/test/face/wind/arti/__init__.py
new file mode 100644
index 00000000..091dbbd5
--- /dev/null
+++ b/test/face/wind/arti/__init__.py
@@ -0,0 +1,440 @@
+yaml_fwol_sample = """
+area: FWOL
+levl: Level 20
+name: Magnificent Tsuba
+rare: 5
+stat:
+  a:
+    data: 12.0
+    name: Crit Rate
+  b:
+    data: 34.0
+    name: Crit DMG
+  c:
+    data: 56.0
+    name: Elemental Mastery
+  d:
+    data: 78.0
+    name: Energy Recharge
+  main:
+    data: 4780.0
+    name: HP
+type: Emblem of Severed Fate
+"""
+
+json_fwol_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "setKey": "EmblemOfSeveredFate",
+  "rarity": 5,
+  "level": 20,
+  "slotKey": "flower",
+  "mainStatKey": "hp",
+  "substats": [
+    {
+      "key": "critRate_",
+      "value": 12.0
+    },
+    {
+      "key": "critDMG_",
+      "value": 34.0
+    },
+    {
+      "key": "eleMas",
+      "value": 56.0
+    },
+    {
+      "key": "enerRech_",
+      "value": 78.0
+    }
+  ]
+}
+"""
+
+actual_fwol = {
+    "area": "fwol",
+    "rare": "Star 5",
+    "levl": "Level 20",
+    "main": "HP",
+    "type": "Emblem of Severed Fate",
+    "name": "Magnificent Tsuba",
+    "stat": {
+        "a": {
+            "name": "Crit Rate",
+            "data": 12.0
+        },
+        "b": {
+            "name": "Crit DMG",
+            "data": 34.0
+        },
+        "c": {
+            "name": "Elemental Mastery",
+            "data": 56.0
+        },
+        "d": {
+            "name": "Energy Recharge",
+            "data": 78.0
+        },
+    }
+}
+
+yaml_pmod_sample = """
+area: PMOD
+levl: Level 20
+name: Sundered Feather
+rare: 5
+stat:
+  a:
+    data: 12.0
+    name: DEF
+  b:
+    data: 34.0
+    name: DEF %
+  c:
+    data: 56.0
+    name: HP
+  d:
+    data: 78.0
+    name: HP %
+  main:
+    data: 311.0
+    name: ATK
+type: Emblem of Severed Fate
+"""
+
+json_pmod_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "setKey": "EmblemOfSeveredFate",
+  "rarity": 5,
+  "level": 20,
+  "slotKey": "plume",
+  "mainStatKey": "atk",
+  "substats": [
+    {
+      "key": "def",
+      "value": 12.0
+    },
+    {
+      "key": "def_",
+      "value": 34.0
+    },
+    {
+      "key": "hp",
+      "value": 56.0
+    },
+    {
+      "key": "hp_",
+      "value": 78.0
+    }
+  ]
+}
+"""
+
+actual_pmod = {
+    "area": "pmod",
+    "rare": "Star 5",
+    "levl": "Level 20",
+    "main": "ATK",
+    "type": "Emblem of Severed Fate",
+    "name": "Sundered Feather",
+    "stat": {
+        "a": {
+            "name": "DEF",
+            "data": 12.0
+        },
+        "b": {
+            "name": "DEF %",
+            "data": 34.0
+        },
+        "c": {
+            "name": "HP",
+            "data": 56.0
+        },
+        "d": {
+            "name": "HP %",
+            "data": 78.0
+        },
+    }
+}
+
+yaml_sdoe_sample = """
+area: SDOE
+levl: Level 20
+name: Morning Dew's Moment
+rare: 5
+stat:
+  a:
+    data: 12.0
+    name: ATK
+  b:
+    data: 34.0
+    name: ATK %
+  c:
+    data: 56.0
+    name: Crit Rate
+  d:
+    data: 78.0
+    name: Crit DMG
+  main:
+    data: 186.5
+    name: Elemental Mastery
+type: Shimenawa's Reminiscence
+"""
+
+json_sdoe_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "setKey": "ShimenawasReminiscence",
+  "rarity": 5,
+  "level": 20,
+  "slotKey": "sands",
+  "mainStatKey": "eleMas",
+  "substats": [
+    {
+      "key": "atk",
+      "value": 12.0
+    },
+    {
+      "key": "atk_",
+      "value": 34.0
+    },
+    {
+      "key": "critRate_",
+      "value": 56.0
+    },
+    {
+      "key": "critDMG_",
+      "value": 78.0
+    }
+  ]
+}
+"""
+
+actual_sdoe = {
+    "area": "sdoe",
+    "rare": "Star 5",
+    "levl": "Level 20",
+    "main": "Elemental Mastery",
+    "type": "Shimenawa's Reminiscence",
+    "name": "Morning Dew's Moment",
+    "stat": {
+        "a": {
+            "name": "ATK",
+            "data": 12.0
+        },
+        "b": {
+            "name": "ATK %",
+            "data": 34.0
+        },
+        "c": {
+            "name": "Crit Rate",
+            "data": 56.0
+        },
+        "d": {
+            "name": "Crit DMG",
+            "data": 78.0
+        },
+    }
+}
+
+yaml_gboe_sample = """
+area: GBOE
+levl: Level 20
+name: Hopeful Heart
+rare: 5
+stat:
+  a:
+    data: 12.0
+    name: Elemental Mastery
+  b:
+    data: 34.0
+    name: Energy Recharge
+  c:
+    data: 56.0
+    name: HP %
+  d:
+    data: 78.0
+    name: HP
+  main:
+    data: 58.3
+    name: Physical DMG Bonus
+type: Shimenawa's Reminiscence
+"""
+
+json_gboe_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "setKey": "ShimenawasReminiscence",
+  "rarity": 5,
+  "level": 20,
+  "slotKey": "goblet",
+  "mainStatKey": "physical_dmg_",
+  "substats": [
+    {
+      "key": "eleMas",
+      "value": 12.0
+    },
+    {
+      "key": "enerRech_",
+      "value": 34.0
+    },
+    {
+      "key": "hp_",
+      "value": 56.0
+    },
+    {
+      "key": "hp",
+      "value": 78.0
+    }
+  ]
+}
+"""
+
+actual_gboe = {
+    "area": "gboe",
+    "rare": "Star 5",
+    "levl": "Level 20",
+    "main": "Physical DMG Bonus",
+    "type": "Shimenawa's Reminiscence",
+    "name": "Hopeful Heart",
+    "stat": {
+        "a": {
+            "name": "Elemental Mastery",
+            "data": 12.0
+        },
+        "b": {
+            "name": "Energy Recharge",
+            "data": 34.0
+        },
+        "c": {
+            "name": "HP %",
+            "data": 56.0
+        },
+        "d": {
+            "name": "HP",
+            "data": 78.0
+        },
+    }
+}
+
+json_gboe_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "setKey": "ShimenawasReminiscence",
+  "rarity": 5,
+  "level": 20,
+  "slotKey": "goblet",
+  "mainStatKey": "physical_dmg_",
+  "substats": [
+    {
+      "key": "eleMas",
+      "value": 12.0
+    },
+    {
+      "key": "enerRech_",
+      "value": 34.0
+    },
+    {
+      "key": "hp_",
+      "value": 56.0
+    },
+    {
+      "key": "hp",
+      "value": 78.0
+    }
+  ]
+}
+"""
+
+yaml_ccol_sample = """
+area: CCOL
+levl: Level 20
+name: Royal Masque
+rare: 5
+stat:
+  a:
+    data: 12.0
+    name: ATK
+  b:
+    data: 34.0
+    name: ATK %
+  c:
+    data: 56.0
+    name: Crit Rate
+  d:
+    data: 78.0
+    name: Crit DMG
+  main:
+    data: 35.9
+    name: Healing Bonus
+type: Noblesse Oblige
+"""
+
+json_ccol_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "setKey": "NoblesseOblige",
+  "rarity": 5,
+  "level": 20,
+  "slotKey": "circlet",
+  "mainStatKey": "heal_",
+  "substats": [
+    {
+      "key": "atk",
+      "value": 12.0
+    },
+    {
+      "key": "atk_",
+      "value": 34.0
+    },
+    {
+      "key": "critRate_",
+      "value": 56.0
+    },
+    {
+      "key": "critDMG_",
+      "value": 78.0
+    }
+  ]
+}
+"""
+
+actual_ccol = {
+    "area": "ccol",
+    "rare": "Star 5",
+    "levl": "Level 20",
+    "main": "Healing Bonus",
+    "type": "Noblesse Oblige",
+    "name": "Royal Masque",
+    "stat": {
+        "a": {
+            "name": "ATK",
+            "data": 12.0
+        },
+        "b": {
+            "name": "ATK %",
+            "data": 34.0
+        },
+        "c": {
+            "name": "Crit Rate",
+            "data": 56.0
+        },
+        "d": {
+            "name": "Crit DMG",
+            "data": 78.0
+        },
+    }
+}
diff --git a/test/face/wind/arti/test_arti.py b/test/face/wind/arti/test_arti.py
new file mode 100644
index 00000000..7dc11e61
--- /dev/null
+++ b/test/face/wind/arti/test_arti.py
@@ -0,0 +1,95 @@
+from random import choice
+
+import pytest
+from PySide6.QtCore import Qt
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts.data.arti import __artilist__
+from gi_loadouts.face.util import truncate_text
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.arti import ArtiLevl, base
+from gi_loadouts.type.stat import STAT
+
+
+@pytest.mark.parametrize(
+    "name, rare, pair, quad, coll",
+    [
+        pytest.param(
+            name, team.rare, team.pairtext, team.quadtext,
+            (team.fwol, team.pmod, team.sdoe, team.gboe, team.ccol),
+            id=f"face.wind.rule: Configuration artifact - {name}"
+        ) for name, team in __artilist__.items()
+    ]
+)
+def test_arti_drop(runner: MainWindow, name: str, rare: list, pair: str, quad: str, coll: tuple) -> None:
+    """
+    Test configuring artifacts on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    conf = dict()
+
+    for area in ["fwol", "pmod", "sdoe", "gboe", "ccol"]:
+        conf[area] = dict()
+        getattr(runner, f"arti_{area}_type").setCurrentText(name)
+        conf[area]["rare"] = choice([item for item in rare])
+        getattr(runner, f"arti_{area}_rare").setCurrentText(conf[area]["rare"].value.name)
+        conf[area]["levl"] = choice([item for item in ArtiLevl if conf[area]["rare"] in item.value.rare])
+        getattr(runner, f"arti_{area}_levl").setCurrentText(conf[area]["levl"].value.name)
+        conf[area]["stat"] = choice([item for item in getattr(base, f"MainStatType_{area.upper()}") if item.value != STAT.none])
+        getattr(runner, f"arti_{area}_name_main").setCurrentText(conf[area]["stat"].value.value)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    if name == "None":
+        return
+
+    for indx, area in enumerate(["fwol", "pmod", "sdoe", "gboe", "ccol"]):
+        item = coll[indx]
+        assert getattr(runner, f"arti_{area}_type_name").text() == truncate_text(coll[indx].__name__, 32)
+        item.rare, item.levl, item.stat_name = conf[area]["rare"].value.qant, conf[area]["levl"].value.levl, conf[area]["stat"]
+        assert getattr(runner, f"arti_{area}_data_main").text() == str(round(item.stat_data, 1))
+
+    assert runner.pair_area_head.text() == f"<b>{truncate_text(name, 26)}</b> (2)"
+    assert runner.quad_area_head.text() == f"<b>{truncate_text(name, 26)}</b> (4)"
+    assert runner.pair_area_desc.toPlainText() == pair
+    assert runner.quad_area_desc.toPlainText() == quad
+
+
+@pytest.mark.parametrize(
+    "area",
+    [
+        pytest.param("fwol", id="face.wind.rule: Clearing the 'Flower of Life' area"),
+        pytest.param("pmod", id="face.wind.rule: Clearing the 'Plume of Death' area"),
+        pytest.param("sdoe", id="face.wind.rule: Clearing the 'Sands of Eon' area"),
+        pytest.param("gboe", id="face.wind.rule: Clearing the 'Goblet of Eonothem' area"),
+        pytest.param("ccol", id="face.wind.rule: Clearing the 'Circlet of Logos' area"),
+    ]
+)
+def test_arti_wipe(runner: MainWindow, qtbot: QtBot, area: str) -> None:
+    """
+    Test configuring characters on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_wipe"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert getattr(runner, f"arti_{area}_type").currentText() == "None"
+    assert getattr(runner, f"arti_{area}_levl").currentText() == "None"
+    assert getattr(runner, f"arti_{area}_rare").currentText() == "Star 0"
+    assert getattr(runner, f"arti_{area}_type_name").text() == "None"
+    for item in ["main", "a", "b", "c", "d"]:
+        assert getattr(runner, f"arti_{area}_name_{item}").currentText() == "None"
+        assert getattr(runner, f"arti_{area}_data_{item}").text() == ""
diff --git a/test/face/wind/arti/test_arti_file.py b/test/face/wind/arti/test_arti_file.py
new file mode 100644
index 00000000..099682c3
--- /dev/null
+++ b/test/face/wind/arti/test_arti_file.py
@@ -0,0 +1,575 @@
+from os import path, remove
+from pathlib import Path
+from random import choice, uniform
+from tempfile import NamedTemporaryFile, gettempdir
+from uuid import uuid4
+
+import pytest
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QFileDialog, QMessageBox
+from pytest_mock import MockerFixture
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts.data.arti import __artilist__
+from gi_loadouts.face.util import truncate_text
+from gi_loadouts.face.wind import file
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.arti import ArtiLevl, base
+from gi_loadouts.type.stat import STAT
+from test import json_type, yaml_type
+
+from . import (
+    actual_ccol,
+    actual_fwol,
+    actual_gboe,
+    actual_pmod,
+    actual_sdoe,
+    json_ccol_sample,
+    json_fwol_sample,
+    json_gboe_sample,
+    json_pmod_sample,
+    json_sdoe_sample,
+    yaml_ccol_sample,
+    yaml_fwol_sample,
+    yaml_gboe_sample,
+    yaml_pmod_sample,
+    yaml_sdoe_sample,
+)
+
+
+@pytest.mark.parametrize(
+    "area",
+    [
+        pytest.param("fwol", id="face.wind.rule: Saving a 'None' artifact from 'Flower of Life' area"),
+        pytest.param("pmod", id="face.wind.rule: Saving a 'None' artifact from 'Plume of Death' area"),
+        pytest.param("sdoe", id="face.wind.rule: Saving a 'None' artifact from 'Sands of Eon' area"),
+        pytest.param("gboe", id="face.wind.rule: Saving a 'None' artifact from 'Goblet of Eonothem' area"),
+        pytest.param("ccol", id="face.wind.rule: Saving a 'None' artifact from 'Circlet of Logos' area"),
+    ]
+)
+def test_arti_save_none(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str) -> None:
+    """
+    Test saving a 'None' artifact across five areas
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_wipe"), Qt.LeftButton)
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "save", return_value=True)
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_save"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "area",
+    [
+        pytest.param("fwol", id="face.wind.rule: Saving an artifact from 'Flower of Life' area"),
+        pytest.param("pmod", id="face.wind.rule: Saving an artifact from 'Plume of Death' area"),
+        pytest.param("sdoe", id="face.wind.rule: Saving an artifact from 'Sands of Eon' area"),
+        pytest.param("gboe", id="face.wind.rule: Saving an artifact from 'Goblet of Eonothem' area"),
+        pytest.param("ccol", id="face.wind.rule: Saving an artifact from 'Circlet of Logos' area"),
+    ]
+)
+def test_arti_save_name(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str) -> None:
+    """
+    Test saving an artifact across five areas
+
+    :return:
+    """
+    conf = dict()
+
+    """
+    Set the user interface elements as intended
+    """
+    name = choice([item for item in __artilist__.keys() if item != "None"])
+    getattr(runner, f"arti_{area}_type").setCurrentText(name)
+    conf["rare"] = choice([item for item in __artilist__[name].rare])
+    getattr(runner, f"arti_{area}_rare").setCurrentText(conf["rare"].value.name)
+    conf["levl"] = choice([item for item in ArtiLevl if conf["rare"] in item.value.rare])
+    getattr(runner, f"arti_{area}_levl").setCurrentText(conf["levl"].value.name)
+    conf["stat"] = choice([item for item in getattr(base, f"MainStatType_{area.upper()}") if item.value != STAT.none])
+    getattr(runner, f"arti_{area}_name_main").setCurrentText(conf["stat"].value.value)
+    for indx in ["a", "b", "c", "d"]:
+        getattr(runner, f"arti_{area}_data_{indx}").setText(str(round(uniform(0, 100), 2)))
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "save", return_value=True)
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_save"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "area",
+    [
+        pytest.param("fwol", id="face.wind.rule: Failing to save an artifact from 'Flower of Life' area"),
+        pytest.param("pmod", id="face.wind.rule: Failing to save an artifact from 'Plume of Death' area"),
+        pytest.param("sdoe", id="face.wind.rule: Failing to save an artifact from 'Sands of Eon' area"),
+        pytest.param("gboe", id="face.wind.rule: Failing to save an artifact from 'Goblet of Eonothem' area"),
+        pytest.param("ccol", id="face.wind.rule: Failing to save an artifact from 'Circlet of Logos' area"),
+    ]
+)
+def test_arti_save_fail(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str) -> None:
+    """
+    Test failing to save an artifact across five areas due to empty data fields
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "save", return_value=True)
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_save"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Save failed"
+    assert "Please confirm that the input is valid (eg. 69, 42.0 etc.) before saving the artifact data in a location that is accessible." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "area, sample, actual",
+    [
+        pytest.param("fwol", yaml_fwol_sample, actual_fwol, id="face.wind.rule: Loading an artifact into 'Flower of Life' area from YAML data"),
+        pytest.param("pmod", yaml_pmod_sample, actual_pmod, id="face.wind.rule: Loading an artifact into 'Plume of Death' area from YAML data"),
+        pytest.param("sdoe", yaml_sdoe_sample, actual_sdoe, id="face.wind.rule: Loading an artifact into 'Sands of Eon' area from YAML data"),
+        pytest.param("gboe", yaml_gboe_sample, actual_gboe, id="face.wind.rule: Loading an artifact into 'Goblet of Eonothem' area from YAML data"),
+        pytest.param("ccol", yaml_ccol_sample, actual_ccol, id="face.wind.rule: Loading an artifact into 'Circlet of Logos' area from YAML data"),
+    ]
+)
+def test_arti_load_yaml(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str, sample: str, actual: dict) -> None:
+    """
+    Test loading an artifact across five areas from YAML data
+
+    :return:
+    """
+
+    """
+    Perform the action of loading the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, yaml_type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_load"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert getattr(runner, f"arti_{area}_type").currentText() == actual["type"]
+    assert getattr(runner, f"arti_{area}_rare").currentText() == actual["rare"]
+    assert getattr(runner, f"arti_{area}_levl").currentText() == actual["levl"]
+    assert getattr(runner, f"arti_{area}_type_name").text() == truncate_text(actual["name"], 32)
+    assert getattr(runner, f"arti_{area}_name_main").currentText() == actual["main"]
+    for item in ["a", "b", "c", "d"]:
+        assert getattr(runner, f"arti_{area}_name_{item}").currentText() == actual["stat"][item]["name"]
+        assert getattr(runner, f"arti_{area}_data_{item}").text() == str(actual["stat"][item]["data"])
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully loaded."
+
+
+@pytest.mark.parametrize(
+    "area, sample, actual",
+    [
+        pytest.param("fwol", json_fwol_sample, actual_fwol, id="face.wind.rule: Loading an artifact into 'Flower of Life' area from JSON data"),
+        pytest.param("pmod", json_pmod_sample, actual_pmod, id="face.wind.rule: Loading an artifact into 'Plume of Death' area from JSON data"),
+        pytest.param("sdoe", json_sdoe_sample, actual_sdoe, id="face.wind.rule: Loading an artifact into 'Sands of Eon' area from JSON data"),
+        pytest.param("gboe", json_gboe_sample, actual_gboe, id="face.wind.rule: Loading an artifact into 'Goblet of Eonothem' area from JSON data"),
+        pytest.param("ccol", json_ccol_sample, actual_ccol, id="face.wind.rule: Loading an artifact into 'Circlet of Logos' area from JSON data"),
+    ]
+)
+def test_arti_load_json(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str, sample: str, actual: dict) -> None:
+    """
+    Test loading an artifact across five areas from JSON data
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, json_type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_load"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert getattr(runner, f"arti_{area}_type").currentText() == actual["type"]
+    assert getattr(runner, f"arti_{area}_rare").currentText() == actual["rare"]
+    assert getattr(runner, f"arti_{area}_levl").currentText() == actual["levl"]
+    assert getattr(runner, f"arti_{area}_type_name").text() == truncate_text(actual["name"], 32)
+    assert getattr(runner, f"arti_{area}_name_main").currentText() == actual["main"]
+    for item in ["a", "b", "c", "d"]:
+        assert getattr(runner, f"arti_{area}_name_{item}").currentText() == actual["stat"][item]["name"]
+        assert getattr(runner, f"arti_{area}_data_{item}").text() == str(actual["stat"][item]["data"])
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully loaded."
+
+
+@pytest.mark.parametrize(
+    "area",
+    [
+        pytest.param("fwol", id="face.wind.rule: Cancelling loading an artifact into 'Flower of Life' area"),
+        pytest.param("pmod", id="face.wind.rule: Cancelling loading an artifact into 'Plume of Death' area"),
+        pytest.param("sdoe", id="face.wind.rule: Cancelling loading an artifact into 'Sands of Eon' area"),
+        pytest.param("gboe", id="face.wind.rule: Cancelling loading an artifact into 'Goblet of Eonothem' area"),
+        pytest.param("ccol", id="face.wind.rule: Cancelling loading an artifact into 'Circlet of Logos' area"),
+    ]
+)
+def test_arti_load_nope(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str) -> None:
+    """
+    Test cancelling loading an artifact across five areas
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(False, "", ""))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_load"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert getattr(runner, f"arti_{area}_type").currentText() == "Adventurer"
+    assert getattr(runner, f"arti_{area}_rare").currentText() == "Star 1"
+    assert getattr(runner, f"arti_{area}_levl").currentText() == "Level 00"
+
+
+@pytest.mark.parametrize(
+    "area",
+    [
+        pytest.param("fwol", id="face.wind.rule: Loading an artifact into 'Flower of Life' area from an empty file"),
+        pytest.param("pmod", id="face.wind.rule: Loading an artifact into 'Plume of Death' area from an empty file"),
+        pytest.param("sdoe", id="face.wind.rule: Loading an artifact into 'Sands of Eon' area from an empty file"),
+        pytest.param("gboe", id="face.wind.rule: Loading an artifact into 'Goblet of Eonothem' area from an empty file"),
+        pytest.param("ccol", id="face.wind.rule: Loading an artifact into 'Circlet of Logos' area from an empty file"),
+    ]
+)
+def test_arti_load_void(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str) -> None:
+    """
+    Test loading an artifact across five areas from an empty file
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, "", ""))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_load"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the artifact data follows the valid format before loading it from a location that is accessible." in runner.dialog.text()
+    assert "Selected file cannot be read." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "area, sample, type",
+    [
+        pytest.param("fwol", json_fwol_sample.replace("fwol", "AWRY"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Flower of Life' area from YAML data"),
+        pytest.param("pmod", json_pmod_sample.replace("pmod", "AWRY"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Plume of Death' area from YAML data"),
+        pytest.param("sdoe", json_sdoe_sample.replace("sdoe", "AWRY"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Sands of Eon' area from YAML data"),
+        pytest.param("gboe", json_gboe_sample.replace("gboe", "AWRY"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Goblet of Eonothem' area from YAML data"),
+        pytest.param("ccol", json_ccol_sample.replace("ccol", "AWRY"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Circlet of Logos' area from YAML data"),
+        pytest.param("fwol", json_fwol_sample.replace("flower", "AWRY"), json_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Flower of Life' area from JSON data"),
+        pytest.param("pmod", json_pmod_sample.replace("plume", "AWRY"), json_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Plume of Death' area from JSON data"),
+        pytest.param("sdoe", json_sdoe_sample.replace("sands", "AWRY"), json_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Sands of Eon' area from JSON data"),
+        pytest.param("gboe", json_gboe_sample.replace("goblet", "AWRY"), json_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Goblet of Eonothem' area from JSON data"),
+        pytest.param("ccol", json_ccol_sample.replace("circlet", "AWRY"), json_type, id="face.wind.rule: Loading an artifact with incorrect area into 'Circlet of Logos' area from JSON data"),
+    ]
+)
+def test_arti_load_awry(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str, sample: str, type: str) -> None:
+    """
+    Test loading an artifact with incorrect area across five areas from data
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_load"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the artifact data follows the valid format before loading it from a location that is accessible." in runner.dialog.text()
+    assert "Artifact unit data cannot be parsed." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "area, sample, type",
+    [
+        pytest.param("fwol", yaml_fwol_sample.replace("name: Crit Rate\n", "name: HP\n"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Flower of Life' area from YAML data"),
+        pytest.param("pmod", yaml_pmod_sample.replace("name: DEF\n", "name: ATK\n"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Plume of Death' area from YAML data"),
+        pytest.param("sdoe", yaml_sdoe_sample.replace("name: ATK\n", "name: Elemental Mastery\n"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Sands of Eon' area from YAML data"),
+        pytest.param("gboe", yaml_gboe_sample.replace("name: Elemental Mastery\n", "name: Physical DMG Bonus\n"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Goblet of Eonothem' area from YAML data"),
+        pytest.param("ccol", yaml_ccol_sample.replace("name: ATK\n", "name: Healing Bonus\n"), yaml_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Circlet of Logos' area from YAML data"),
+        pytest.param("fwol", json_fwol_sample.replace("\"key\": \"critRate_\",\n", "\"key\": \"hp\",\n"), json_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Flower of Life' area from JSON data"),
+        pytest.param("pmod", json_pmod_sample.replace("\"key\": \"def\",\n", "\"key\": \"atk\",\n"), json_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Plume of Death' area from JSON data"),
+        pytest.param("sdoe", json_sdoe_sample.replace("\"key\": \"atk\",\n", "\"key\": \"eleMas\",\n"), json_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Sands of Eon' area from JSON data"),
+        pytest.param("gboe", json_gboe_sample.replace("\"key\": \"eleMas\",\n", "\"key\": \"physical_dmg_\",\n"), json_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Goblet of Eonothem' area from JSON data"),
+        pytest.param("ccol", json_ccol_sample.replace("\"key\": \"atk\",\n", "\"key\": \"heal_\",\n"), json_type, id="face.wind.rule: Loading an artifact with incorrect substats into 'Circlet of Logos' area from JSON data"),
+    ]
+)
+def test_arti_load_name(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str, sample: str, type: str) -> None:
+    """
+    Test loading an artifact with incorrect substats across five areas from data
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_load"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the artifact data follows the valid format before loading it from a location that is accessible." in runner.dialog.text()
+    assert "Artifact stat cannot be identified." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "area",
+    [
+        pytest.param("fwol", id="face.wind.rule: Saving an artifact from 'Flower of Life' area actually as a YAML file"),
+        pytest.param("pmod", id="face.wind.rule: Saving an artifact from 'Plume of Death' area actually as a YAML file"),
+        pytest.param("sdoe", id="face.wind.rule: Saving an artifact from 'Sands of Eon' area actually as a YAML file"),
+        pytest.param("gboe", id="face.wind.rule: Saving an artifact from 'Goblet of Eonothem' area actually as a YAML file"),
+        pytest.param("ccol", id="face.wind.rule: Saving an artifact from 'Circlet of Logos' area actually as a YAML file"),
+    ]
+)
+def test_arti_save_yaml_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str) -> None:
+    """
+    Test saving an artifact across five areas
+
+    :return:
+    """
+    conf = dict()
+
+    """
+    Set the user interface elements as intended
+    """
+    name = choice([item for item in __artilist__.keys() if item != "None"])
+    getattr(runner, f"arti_{area}_type").setCurrentText(name)
+    conf["rare"] = choice([item for item in __artilist__[name].rare])
+    getattr(runner, f"arti_{area}_rare").setCurrentText(conf["rare"].value.name)
+    conf["levl"] = choice([item for item in ArtiLevl if conf["rare"] in item.value.rare])
+    getattr(runner, f"arti_{area}_levl").setCurrentText(conf["levl"].value.name)
+    conf["stat"] = choice([item for item in getattr(base, f"MainStatType_{area.upper()}") if item.value != STAT.none])
+    getattr(runner, f"arti_{area}_name_main").setCurrentText(conf["stat"].value.value)
+    for indx in ["a", "b", "c", "d"]:
+        getattr(runner, f"arti_{area}_data_{indx}").setText(str(round(uniform(0, 100), 2)))
+
+    """
+    Perform the action of saving the artifact information
+    """
+    extn = ".yaml"
+
+    """
+    Without extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper())
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, yaml_type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_save"), Qt.LeftButton)
+    if path.exists(temp + extn):
+        remove(temp + extn)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully saved."
+
+    """
+    With extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper()) + extn
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, yaml_type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_save"), Qt.LeftButton)
+    if path.exists(temp):
+        remove(temp)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "area",
+    [
+        pytest.param("fwol", id="face.wind.rule: Saving an artifact from 'Flower of Life' area actually as a JSON file"),
+        pytest.param("pmod", id="face.wind.rule: Saving an artifact from 'Plume of Death' area actually as a JSON file"),
+        pytest.param("sdoe", id="face.wind.rule: Saving an artifact from 'Sands of Eon' area actually as a JSON file"),
+        pytest.param("gboe", id="face.wind.rule: Saving an artifact from 'Goblet of Eonothem' area actually as a JSON file"),
+        pytest.param("ccol", id="face.wind.rule: Saving an artifact from 'Circlet of Logos' area actually as a JSON file"),
+    ]
+)
+def test_arti_save_json_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str) -> None:
+    """
+    Test saving an artifact across five areas
+
+    :return:
+    """
+    conf = dict()
+
+    """
+    Set the user interface elements as intended
+    """
+    name = choice([item for item in __artilist__.keys() if item != "None"])
+    getattr(runner, f"arti_{area}_type").setCurrentText(name)
+    conf["rare"] = choice([item for item in __artilist__[name].rare])
+    getattr(runner, f"arti_{area}_rare").setCurrentText(conf["rare"].value.name)
+    conf["levl"] = choice([item for item in ArtiLevl if conf["rare"] in item.value.rare])
+    getattr(runner, f"arti_{area}_levl").setCurrentText(conf["levl"].value.name)
+    conf["stat"] = choice([item for item in getattr(base, f"MainStatType_{area.upper()}") if item.value != STAT.none])
+    getattr(runner, f"arti_{area}_name_main").setCurrentText(conf["stat"].value.value)
+    for indx in ["a", "b", "c", "d"]:
+        getattr(runner, f"arti_{area}_data_{indx}").setText(str(round(uniform(0, 100), 2)))
+
+    """
+    Perform the action of saving the artifact information
+    """
+    extn = ".json"
+
+    """
+    Without extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper())
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, json_type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_save"), Qt.LeftButton)
+    if path.exists(temp + extn):
+        remove(temp + extn)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully saved."
+
+    """
+    With extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper()) + extn
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, json_type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_save"), Qt.LeftButton)
+    if path.exists(temp):
+        remove(temp)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "area, sample",
+    [
+        pytest.param("fwol", yaml_fwol_sample, id="face.wind.rule: Loading an artifact from 'Flower of Life' area actually as a YAML file"),
+        pytest.param("pmod", yaml_pmod_sample, id="face.wind.rule: Loading an artifact from 'Plume of Death' area actually as a YAML file"),
+        pytest.param("sdoe", yaml_sdoe_sample, id="face.wind.rule: Loading an artifact from 'Sands of Eon' area actually as a YAML file"),
+        pytest.param("gboe", yaml_gboe_sample, id="face.wind.rule: Loading an artifact from 'Goblet of Eonothem' area actually as a YAML file"),
+        pytest.param("ccol", yaml_ccol_sample, id="face.wind.rule: Loading an artifact from 'Circlet of Logos' area actually as a YAML file"),
+    ]
+)
+def test_arti_load_yaml_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str, sample: str) -> None:
+    """
+    Test loading an artifact across five areas
+
+    :return:
+    """
+
+    """
+    Perform the action of loading the artifact information
+    """
+    extn = ".yaml"
+    temp = NamedTemporaryFile(prefix="gi-loadouts-", suffix=extn, delete=False, mode="w")
+    temp.write(sample)
+    temp.close()
+
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(temp.name, yaml_type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_load"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully loaded."
+
+    """
+    Cleanup the temporary files
+    """
+    remove(temp.name)
+
+
+@pytest.mark.parametrize(
+    "area, sample",
+    [
+        pytest.param("fwol", json_fwol_sample, id="face.wind.rule: Loading an artifact from 'Flower of Life' area actually as a JSON file"),
+        pytest.param("pmod", json_pmod_sample, id="face.wind.rule: Loading an artifact from 'Plume of Death' area actually as a JSON file"),
+        pytest.param("sdoe", json_sdoe_sample, id="face.wind.rule: Loading an artifact from 'Sands of Eon' area actually as a JSON file"),
+        pytest.param("gboe", json_gboe_sample, id="face.wind.rule: Loading an artifact from 'Goblet of Eonothem' area actually as a JSON file"),
+        pytest.param("ccol", json_ccol_sample, id="face.wind.rule: Loading an artifact from 'Circlet of Logos' area actually as a JSON file"),
+    ]
+)
+def test_arti_load_json_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, area: str, sample: str) -> None:
+    """
+    Test loading an artifact across five areas
+
+    :return:
+    """
+
+    """
+    Perform the action of loading the artifact information
+    """
+    extn = ".json"
+    temp = NamedTemporaryFile(prefix="gi-loadouts-", suffix=extn, delete=False, mode="w")
+    temp.write(sample)
+    temp.close()
+
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(temp.name, json_type))
+    qtbot.mouseClick(getattr(runner, f"arti_{area}_load"), Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact data has been successfully loaded."
+
+    """
+    Cleanup the temporary files
+    """
+    remove(temp.name)
diff --git a/test/face/wind/char/__init__.py b/test/face/wind/char/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/face/wind/char/test_char.py b/test/face/wind/char/test_char.py
new file mode 100644
index 00000000..b498c274
--- /dev/null
+++ b/test/face/wind/char/test_char.py
@@ -0,0 +1,125 @@
+import pytest
+from PySide6.QtCore import Qt
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts.data.char import __charmaps__
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.char.cons import Cons
+from gi_loadouts.type.levl import Level
+
+
+@pytest.mark.parametrize(
+    "name",
+    [
+        pytest.param(
+            name,
+            id=f"face.wind.rule: Configuring character - {name}"
+        ) for name in __charmaps__.keys()
+    ]
+)
+def test_char_drop(runner: MainWindow, name: str) -> None:
+    """
+    Test configuring characters on the user interface
+
+    :return:
+    """
+
+    char = __charmaps__[name]()
+    char.levl = Level.Level_80_90_Rank_6
+    char.cons = Cons.Constellation_6
+
+    vson = char.vision.value.name
+    cons = char.cons.value.name
+    levl = char.levl.value.name
+    rare = char.rare.value.qant
+    cons_name = char.cons_name
+    afln = char.afln
+    weap = char.weapon.value
+    attk = round(char.attack.stat_data)
+    dfns = round(char.defense.stat_data)
+    hlpt = round(char.health_points.stat_data)
+    stat = char.seco.stat_data
+    seco = char.__statname__.value
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_elem.setCurrentText(vson.title())
+    runner.head_char_name.setCurrentText(name)
+    runner.head_char_levl.setCurrentText(levl)
+    runner.head_char_cons.setCurrentText(cons.replace(" ","_"))
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.head_area_line_prim.text() == name
+    assert runner.char_area_rare.text() == " ".join(["STAR"] * rare)
+    assert runner.head_area_line_seco.text() == cons_name
+    assert runner.head_area_line_tert.text() == weap
+    assert runner.head_area_line_quat.text() == f"<i>{name} is a {weap.lower()}-wielding {vson + " " if vson.lower() != "none" else ""}character of {rare}-star quality.</i>"
+    assert runner.head_area_line_quin.text() == f"<i>{name} is affiliated with {afln}.</i>" if afln != "" else f"<i>{name} is not affiliated with any association.</i>"
+    assert int(runner.head_char_data_attk.text()) == attk
+    assert int(runner.head_char_data_dfns.text()) == dfns
+    assert int(runner.head_char_data_hlpt.text()) == hlpt
+    assert float(runner.head_char_data_subs.text()) == stat
+    assert runner.head_char_icon_attk.toolTip() == "ATK"
+    assert runner.head_char_icon_dfns.toolTip() == "DEF"
+    assert runner.head_char_icon_hlpt.toolTip() == "HP"
+    assert runner.head_char_icon_subs.toolTip() == seco
+
+
+@pytest.mark.parametrize(
+    "name, butn",
+    [
+        pytest.param(
+            name, butn,
+            id=f"face.wind.rule: Configuring character - {name}"
+        ) for name, butn in {"Lumine": "char_head_lumi", "Aether": "char_head_aeth"}.items()
+    ]
+)
+def test_char_butn(runner: MainWindow, qtbot: QtBot, name: str, butn: str) -> None:
+    """
+    Test configuring travelers on the user interface
+
+    :return:
+    """
+
+    char = __charmaps__[name]()
+    char.levl = Level.Level_80_90_Rank_6
+    char.cons = Cons.Constellation_6
+
+    cons = char.cons.value.name
+    levl = char.levl.value.name
+    rare = char.rare.value.qant
+    cons_name = char.cons_name
+    weap = char.weapon.value
+    attk = round(char.attack.stat_data)
+    dfns = round(char.defense.stat_data)
+    hlpt = round(char.health_points.stat_data)
+    stat = char.seco.stat_data
+    seco = char.__statname__.value
+
+    """
+    Set the user interface elements as intended
+    """
+    qtbot.mouseClick(getattr(runner, butn), Qt.LeftButton)
+    runner.head_char_levl.setCurrentText(levl)
+    runner.head_char_cons.setCurrentText(cons.replace(" ","_"))
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.head_area_line_prim.text() == name
+    assert runner.char_area_rare.text() == " ".join(["STAR"] * rare)
+    assert runner.head_area_line_seco.text() == cons_name
+    assert runner.head_area_line_tert.text() == weap
+    assert runner.head_area_line_quat.text() == f"<i>{name} is a {weap.lower()}-wielding character of {rare}-star quality.</i>"
+    assert runner.head_area_line_quin.text() == f"<i>{name} is not affiliated with any association.</i>"
+    assert int(runner.head_char_data_attk.text()) == attk
+    assert int(runner.head_char_data_dfns.text()) == dfns
+    assert int(runner.head_char_data_hlpt.text()) == hlpt
+    assert float(runner.head_char_data_subs.text()) == stat
+    assert runner.head_char_icon_attk.toolTip() == "ATK"
+    assert runner.head_char_icon_dfns.toolTip() == "DEF"
+    assert runner.head_char_icon_hlpt.toolTip() == "HP"
+    assert runner.head_char_icon_subs.toolTip() == seco
diff --git a/test/face/wind/team/__init__.py b/test/face/wind/team/__init__.py
new file mode 100644
index 00000000..68d1427f
--- /dev/null
+++ b/test/face/wind/team/__init__.py
@@ -0,0 +1,267 @@
+from test.face.wind.arti import actual_ccol, actual_fwol, actual_gboe, actual_pmod, actual_sdoe
+
+yaml_sample = """
+ccol:
+  area: CCOL
+  levl: Level 20
+  name: Royal Masque
+  rare: 5
+  stat:
+    a:
+      data: 12.0
+      name: ATK
+    b:
+      data: 34.0
+      name: ATK %
+    c:
+      data: 56.0
+      name: Crit Rate
+    d:
+      data: 78.0
+      name: Crit DMG
+    main:
+      data: 35.9
+      name: Healing Bonus
+  type: Noblesse Oblige
+fwol:
+  area: FWOL
+  levl: Level 20
+  name: Magnificent Tsuba
+  rare: 5
+  stat:
+    a:
+      data: 12.0
+      name: Crit Rate
+    b:
+      data: 34.0
+      name: Crit DMG
+    c:
+      data: 56.0
+      name: Elemental Mastery
+    d:
+      data: 78.0
+      name: Energy Recharge
+    main:
+      data: 4780.0
+      name: HP
+  type: Emblem of Severed Fate
+gboe:
+  area: GBOE
+  levl: Level 20
+  name: Hopeful Heart
+  rare: 5
+  stat:
+    a:
+      data: 12.0
+      name: Elemental Mastery
+    b:
+      data: 34.0
+      name: Energy Recharge
+    c:
+      data: 56.0
+      name: HP %
+    d:
+      data: 78.0
+      name: HP
+    main:
+      data: 58.3
+      name: Physical DMG Bonus
+  type: Shimenawa's Reminiscence
+pmod:
+  area: PMOD
+  levl: Level 20
+  name: Sundered Feather
+  rare: 5
+  stat:
+    a:
+      data: 12.0
+      name: DEF
+    b:
+      data: 34.0
+      name: DEF %
+    c:
+      data: 56.0
+      name: HP
+    d:
+      data: 78.0
+      name: HP %
+    main:
+      data: 311.0
+      name: ATK
+  type: Emblem of Severed Fate
+sdoe:
+  area: SDOE
+  levl: Level 20
+  name: Morning Dew's Moment
+  rare: 5
+  stat:
+    a:
+      data: 12.0
+      name: ATK
+    b:
+      data: 34.0
+      name: ATK %
+    c:
+      data: 56.0
+      name: Crit Rate
+    d:
+      data: 78.0
+      name: Crit DMG
+    main:
+      data: 186.5
+      name: Elemental Mastery
+  type: Shimenawa's Reminiscence
+"""
+
+json_sample = """
+{
+  "flower": {
+    "id": "9AA6DAE4",
+    "location": "",
+    "lock": false,
+    "setKey": "EmblemOfSeveredFate",
+    "rarity": 5,
+    "level": 20,
+    "slotKey": "flower",
+    "mainStatKey": "hp",
+    "substats": [
+      {
+        "key": "critRate_",
+        "value": 12.0
+      },
+      {
+        "key": "critDMG_",
+        "value": 34.0
+      },
+      {
+        "key": "eleMas",
+        "value": 56.0
+      },
+      {
+        "key": "enerRech_",
+        "value": 78.0
+      }
+    ]
+  },
+  "plume": {
+    "id": "2FDCFA98",
+    "location": "",
+    "lock": false,
+    "setKey": "EmblemOfSeveredFate",
+    "rarity": 5,
+    "level": 20,
+    "slotKey": "plume",
+    "mainStatKey": "atk",
+    "substats": [
+      {
+        "key": "def",
+        "value": 12.0
+      },
+      {
+        "key": "def_",
+        "value": 34.0
+      },
+      {
+        "key": "hp",
+        "value": 56.0
+      },
+      {
+        "key": "hp_",
+        "value": 78.0
+      }
+    ]
+  },
+  "sands": {
+    "id": "9D28FAFF",
+    "location": "",
+    "lock": false,
+    "setKey": "ShimenawasReminiscence",
+    "rarity": 5,
+    "level": 20,
+    "slotKey": "sands",
+    "mainStatKey": "eleMas",
+    "substats": [
+      {
+        "key": "atk",
+        "value": 12.0
+      },
+      {
+        "key": "atk_",
+        "value": 34.0
+      },
+      {
+        "key": "critRate_",
+        "value": 56.0
+      },
+      {
+        "key": "critDMG_",
+        "value": 78.0
+      }
+    ]
+  },
+  "goblet": {
+    "id": "74541CF4",
+    "location": "",
+    "lock": false,
+    "setKey": "ShimenawasReminiscence",
+    "rarity": 5,
+    "level": 20,
+    "slotKey": "goblet",
+    "mainStatKey": "physical_dmg_",
+    "substats": [
+      {
+        "key": "eleMas",
+        "value": 12.0
+      },
+      {
+        "key": "enerRech_",
+        "value": 34.0
+      },
+      {
+        "key": "hp_",
+        "value": 56.0
+      },
+      {
+        "key": "hp",
+        "value": 78.0
+      }
+    ]
+  },
+  "circlet": {
+    "id": "B0D283FC",
+    "location": "",
+    "lock": false,
+    "setKey": "NoblesseOblige",
+    "rarity": 5,
+    "level": 20,
+    "slotKey": "circlet",
+    "mainStatKey": "heal_",
+    "substats": [
+      {
+        "key": "atk",
+        "value": 12.0
+      },
+      {
+        "key": "atk_",
+        "value": 34.0
+      },
+      {
+        "key": "critRate_",
+        "value": 56.0
+      },
+      {
+        "key": "critDMG_",
+        "value": 78.0
+      }
+    ]
+  }
+}
+"""
+
+actual = {
+    "fwol": actual_fwol,
+    "pmod": actual_pmod,
+    "sdoe": actual_sdoe,
+    "gboe": actual_gboe,
+    "ccol": actual_ccol
+}
diff --git a/test/face/wind/team/test_team.py b/test/face/wind/team/test_team.py
new file mode 100644
index 00000000..3bbfc8da
--- /dev/null
+++ b/test/face/wind/team/test_team.py
@@ -0,0 +1,39 @@
+import pytest
+from PySide6.QtCore import Qt
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts.face.wind.main import MainWindow
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.wind.rule: Wiping the artifact collection"
+        )
+    ]
+)
+def test_team_wipe(runner: MainWindow, qtbot: QtBot, _: None) -> None:
+    """
+    Test configuring characters on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    qtbot.mouseClick(runner.head_wipe, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    for area in ["fwol", "pmod", "sdoe", "gboe", "ccol"]:
+        assert getattr(runner, f"arti_{area}_type").currentText() == "None"
+        assert getattr(runner, f"arti_{area}_levl").currentText() == "None"
+        assert getattr(runner, f"arti_{area}_rare").currentText() == "Star 0"
+        assert getattr(runner, f"arti_{area}_type_name").text() == "None"
+        for item in ["main", "a", "b", "c", "d"]:
+            assert getattr(runner, f"arti_{area}_name_{item}").currentText() == "None"
+            assert getattr(runner, f"arti_{area}_data_{item}").text() == ""
diff --git a/test/face/wind/team/test_team_file.py b/test/face/wind/team/test_team_file.py
new file mode 100644
index 00000000..013974b6
--- /dev/null
+++ b/test/face/wind/team/test_team_file.py
@@ -0,0 +1,565 @@
+from os import path, remove
+from pathlib import Path
+from tempfile import NamedTemporaryFile, gettempdir
+from uuid import uuid4
+
+import pytest
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QFileDialog, QMessageBox
+from pytest_mock import MockerFixture
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts.face.wind import file
+from gi_loadouts.face.wind.main import MainWindow
+from test import json_type, yaml_type
+
+from . import actual, json_sample, yaml_sample
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.wind.rule: Saving a 'None' artifact collection"
+        )
+    ]
+)
+def test_team_save_none(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test saving a 'None' artifact collection
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    qtbot.mouseClick(runner.head_wipe, Qt.LeftButton)
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "save", return_value=True)
+    qtbot.mouseClick(runner.head_save, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.wind.rule: Saving an artifact collection"
+        )
+    ]
+)
+def test_team_save_name(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test saving an artifact across five areas
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    for area, data in actual.items():
+        getattr(runner, f"arti_{area}_type").setCurrentText(data["type"])
+        getattr(runner, f"arti_{area}_rare").setCurrentText(data["rare"])
+        getattr(runner, f"arti_{area}_levl").setCurrentText(data["levl"])
+        getattr(runner, f"arti_{area}_name_main").setCurrentText(data["main"])
+        for item, stat in data["stat"].items():
+            getattr(runner, f"arti_{area}_name_{item}").setCurrentText(stat["name"])
+            getattr(runner, f"arti_{area}_data_{item}").setText(str(stat["data"]))
+
+    """
+    Perform the action of saving the artifact collection information
+    """
+    mocker.patch.object(file.FileHandling, "save", return_value=True)
+    qtbot.mouseClick(runner.head_save, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.wind.rule: Failing to save the artifact collection"
+        )
+    ]
+)
+def test_team_save_fail(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test failing to save an artifact collection due to empty data fields
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact collection information
+    """
+    mocker.patch.object(file.FileHandling, "save", return_value=True)
+    qtbot.mouseClick(runner.head_save, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Save failed"
+    assert "Please confirm that the input is valid (eg. 69, 42.0 etc.) before saving the artifact set in a location that is accessible." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.wind.rule: Loading the artifact collection from YAML data"
+        )
+    ]
+)
+def test_team_load_yaml(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test loading an artifact collection from YAML data
+
+    :return:
+    """
+
+    """
+    Perform the action of loading the artifact collection information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, yaml_sample, yaml_type))
+    qtbot.mouseClick(runner.head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    for area, data in actual.items():
+        assert getattr(runner, f"arti_{area}_type").currentText() == data["type"]
+        assert getattr(runner, f"arti_{area}_rare").currentText() == data["rare"]
+        assert getattr(runner, f"arti_{area}_levl").currentText() == data["levl"]
+        assert getattr(runner, f"arti_{area}_name_main").currentText() == data["main"]
+        for item, stat in data["stat"].items():
+            assert getattr(runner, f"arti_{area}_name_{item}").currentText() == stat["name"]
+            assert getattr(runner, f"arti_{area}_data_{item}").text() == str(stat["data"])
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully loaded."
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.wind.rule: Loading the artifact collection from JSON data"
+        )
+    ]
+)
+def test_team_load_json(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test loading an artifact collection from JSON data
+
+    :return:
+    """
+
+    """
+    Perform the action of loading the artifact collection information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, json_sample, json_type))
+    qtbot.mouseClick(runner.head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    for area, data in actual.items():
+        assert getattr(runner, f"arti_{area}_type").currentText() == data["type"]
+        assert getattr(runner, f"arti_{area}_rare").currentText() == data["rare"]
+        assert getattr(runner, f"arti_{area}_levl").currentText() == data["levl"]
+        assert getattr(runner, f"arti_{area}_name_main").currentText() == data["main"]
+        for item, stat in data["stat"].items():
+            assert getattr(runner, f"arti_{area}_name_{item}").currentText() == stat["name"]
+            assert getattr(runner, f"arti_{area}_data_{item}").text() == str(stat["data"])
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully loaded."
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.wind.rule: Cancelling loading the artifact collection"
+        )
+    ]
+)
+def test_team_load_nope(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test cancelling loading an artifact collection
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(False, "", ""))
+    qtbot.mouseClick(runner.head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    for area in actual.keys():
+        assert getattr(runner, f"arti_{area}_type").currentText() == "Adventurer"
+        assert getattr(runner, f"arti_{area}_rare").currentText() == "Star 1"
+        assert getattr(runner, f"arti_{area}_levl").currentText() == "Level 00"
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(
+            None,
+            id="face.wind.rule: Loading the artifact collection from an empty file"
+        )
+    ]
+)
+def test_team_load_void(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test loading an artifact collection from an empty file
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, "", ""))
+    qtbot.mouseClick(runner.head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the artifact set follows the valid format before loading it from a location that is accessible." in runner.dialog.text()
+    assert "Selected file cannot be read." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "sample, type",
+    [
+        pytest.param(
+            yaml_sample.replace("fwol", "AWRY").replace("pmod", "AWRY").replace("sdoe", "AWRY").replace("gboe", "AWRY").replace("ccol", "AWRY"),
+            "YAML Files (*.yaml)",
+            id="face.wind.rule: Loading the artifact collection with incorrect areas from YAML data"
+        ),
+        pytest.param(
+            json_sample.replace("flower", "AWRY").replace("plume", "AWRY").replace("sands", "AWRY").replace("goblet", "AWRY").replace("circlet", "AWRY"),
+            "GOOD Files (*.json)",
+            id="face.wind.rule: Loading the artifact collection with incorrect areas from JSON data"
+        ),
+    ]
+)
+def test_team_load_awry(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, sample: str, type: str) -> None:
+    """
+    Test loading an artifact collection with incorrect areas from data
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, type))
+    qtbot.mouseClick(runner.head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the artifact set follows the valid format before loading it from a location that is accessible." in runner.dialog.text()
+    assert "Artifact set data cannot be parsed." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "sample, type",
+    [
+        pytest.param(
+            yaml_sample.
+            replace("name: Crit Rate\n", "name: HP\n").
+            replace("name: DEF\n", "name: ATK\n").
+            replace("name: ATK\n", "name: Elemental Mastery\n").
+            replace("name: Elemental Mastery\n", "name: Physical DMG Bonus\n").
+            replace("name: ATK\n", "name: Healing Bonus\n"),
+            yaml_type,
+            id="face.wind.rule: Loading an artifact with incorrect substats from YAML data"
+        ),
+        pytest.param(
+            json_sample.
+            replace("\"key\": \"critRate_\",\n", "\"key\": \"hp\",\n").
+            replace("\"key\": \"def\",\n", "\"key\": \"atk\",\n").
+            replace("\"key\": \"atk\",\n", "\"key\": \"eleMas\",\n").
+            replace("\"key\": \"eleMas\",\n", "\"key\": \"physical_dmg_\",\n").
+            replace("\"key\": \"atk\",\n", "\"key\": \"heal_\",\n"),
+            json_type,
+            id="face.wind.rule: Loading an artifact with incorrect substats from JSON data"
+        ),
+    ]
+)
+def test_team_load_name(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, sample: str, type: str) -> None:
+    """
+    Test loading an artifact collection with incorrect substats from data
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the artifact information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, type))
+    qtbot.mouseClick(runner.head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the artifact set follows the valid format before loading it from a location that is accessible." in runner.dialog.text()
+    assert "Artifact stat cannot be identified." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.wind.rule: Saving an artifact collection as a YAML file")
+    ]
+)
+def test_team_save_yaml_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test saving an artifact across five areas as a YAML file
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    for area, data in actual.items():
+        getattr(runner, f"arti_{area}_type").setCurrentText(data["type"])
+        getattr(runner, f"arti_{area}_rare").setCurrentText(data["rare"])
+        getattr(runner, f"arti_{area}_levl").setCurrentText(data["levl"])
+        getattr(runner, f"arti_{area}_name_main").setCurrentText(data["main"])
+        for item, stat in data["stat"].items():
+            getattr(runner, f"arti_{area}_name_{item}").setCurrentText(stat["name"])
+            getattr(runner, f"arti_{area}_data_{item}").setText(str(stat["data"]))
+
+    """
+    Perform the action of saving the artifact information
+    """
+    extn = ".yaml"
+
+    """
+    Without extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper())
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, yaml_type))
+    qtbot.mouseClick(runner.head_save, Qt.LeftButton)
+    if path.exists(temp + extn):
+        remove(temp + extn)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully saved."
+
+    """
+    With extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper()) + extn
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, yaml_type))
+    qtbot.mouseClick(runner.head_save, Qt.LeftButton)
+    if path.exists(temp):
+        remove(temp)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.wind.rule: Saving an artifact collection as a JSON file")
+    ]
+)
+def test_team_save_json_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, _: None) -> None:
+    """
+    Test saving an artifact across five areas as a JSON file
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    for area, data in actual.items():
+        getattr(runner, f"arti_{area}_type").setCurrentText(data["type"])
+        getattr(runner, f"arti_{area}_rare").setCurrentText(data["rare"])
+        getattr(runner, f"arti_{area}_levl").setCurrentText(data["levl"])
+        getattr(runner, f"arti_{area}_name_main").setCurrentText(data["main"])
+        for item, stat in data["stat"].items():
+            getattr(runner, f"arti_{area}_name_{item}").setCurrentText(stat["name"])
+            getattr(runner, f"arti_{area}_data_{item}").setText(str(stat["data"]))
+
+    """
+    Perform the action of saving the artifact information
+    """
+    extn = ".json"
+
+    """
+    Without extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper())
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, json_type))
+    qtbot.mouseClick(runner.head_save, Qt.LeftButton)
+    if path.exists(temp + extn):
+        remove(temp + extn)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully saved."
+
+    """
+    With extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper()) + extn
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, json_type))
+    qtbot.mouseClick(runner.head_save, Qt.LeftButton)
+    if path.exists(temp):
+        remove(temp)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "sample",
+    [
+        pytest.param(yaml_sample, id="face.wind.rule: Loading the actual artifact collection from YAML data")
+    ]
+)
+def test_team_load_yaml_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, sample: str) -> None:
+    """
+    Test loading an artifact collection from YAML data
+
+    :return:
+    """
+
+    """
+    Perform the action of loading the artifact collection information
+    """
+    extn = ".yaml"
+    temp = NamedTemporaryFile(prefix="gi-loadouts-", suffix=extn, delete=False, mode="w")
+    temp.write(sample)
+    temp.close()
+
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(temp.name, yaml_type))
+    qtbot.mouseClick(runner.head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    for area, data in actual.items():
+        assert getattr(runner, f"arti_{area}_type").currentText() == data["type"]
+        assert getattr(runner, f"arti_{area}_rare").currentText() == data["rare"]
+        assert getattr(runner, f"arti_{area}_levl").currentText() == data["levl"]
+        assert getattr(runner, f"arti_{area}_name_main").currentText() == data["main"]
+        for item, stat in data["stat"].items():
+            assert getattr(runner, f"arti_{area}_name_{item}").currentText() == stat["name"]
+            assert getattr(runner, f"arti_{area}_data_{item}").text() == str(stat["data"])
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully loaded."
+
+    """
+    Cleanup the temporary files
+    """
+    remove(temp.name)
+
+
+@pytest.mark.parametrize(
+    "sample",
+    [
+        pytest.param(json_sample, id="face.wind.rule: Loading the actual artifact collection from JSON data")
+    ]
+)
+def test_team_load_json_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, sample: str) -> None:
+    """
+    Test loading an artifact collection from JSON data
+
+    :return:
+    """
+
+    """
+    Perform the action of loading the artifact collection information
+    """
+    extn = ".json"
+    temp = NamedTemporaryFile(prefix="gi-loadouts-", suffix=extn, delete=False, mode="w")
+    temp.write(sample)
+    temp.close()
+
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(temp.name, json_sample))
+    qtbot.mouseClick(runner.head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    for area, data in actual.items():
+        assert getattr(runner, f"arti_{area}_type").currentText() == data["type"]
+        assert getattr(runner, f"arti_{area}_rare").currentText() == data["rare"]
+        assert getattr(runner, f"arti_{area}_levl").currentText() == data["levl"]
+        assert getattr(runner, f"arti_{area}_name_main").currentText() == data["main"]
+        for item, stat in data["stat"].items():
+            assert getattr(runner, f"arti_{area}_name_{item}").currentText() == stat["name"]
+            assert getattr(runner, f"arti_{area}_data_{item}").text() == str(stat["data"])
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Artifact set has been successfully loaded."
+
+    """
+    Cleanup the temporary files
+    """
+    remove(temp.name)
diff --git a/test/face/wind/test_main.py b/test/face/wind/test_main.py
new file mode 100644
index 00000000..334d1f23
--- /dev/null
+++ b/test/face/wind/test_main.py
@@ -0,0 +1,131 @@
+from os import path, remove
+from tempfile import NamedTemporaryFile
+
+import pytest
+from PySide6.QtCore import Qt, QUrl
+from PySide6.QtGui import QDesktopServices
+from PySide6.QtWidgets import QMessageBox
+from pytest_mock import MockerFixture
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts import __donation__, __homepage__, __issutckt__, conf
+from gi_loadouts.face.rsrc import make_temp_file
+from gi_loadouts.face.wind.main import MainWindow
+from test.face.wind import MockQFile
+
+
+@pytest.mark.parametrize(
+    "button, link",
+    [
+        pytest.param("side_head", __homepage__, id="face.info: Clicking the home button"),
+        pytest.param("side_tckt", __issutckt__, id="face.info: Clicking the report button"),
+        pytest.param("side_cash", __donation__, id="face.info: Clicking the donate button")
+    ]
+)
+def test_wind_main_buttons(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, button: str, link: str) -> None:
+    """
+    Test clicking the buttons on side of UI
+
+    :return:
+    """
+
+    """
+    Perform the action of clicking the button
+    """
+    mock_open_url = mocker.patch.object(QDesktopServices, 'openUrl')
+    qtbot.mouseClick(getattr(runner, f"{button}"), Qt.LeftButton)
+    expected_url = QUrl(link)
+
+    """
+    Confirm if the link is opened
+    """
+    mock_open_url.assert_called_once_with(expected_url)
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.wind: Initialise the training data for Tesseract OCR")
+    ]
+)
+def test_wind_main_fail(mocker: MockerFixture, _: None) -> None:
+    """
+    Test failing initialisation of training data for Tesseract OCR
+
+    :return:
+    """
+
+    """
+    Mock QFile to return false when open method is called and initiate runner after the mocking
+    """
+    with NamedTemporaryFile(prefix="gi-loadouts-", suffix=".traineddata", delete=False) as savefile:
+        savefile.write(b"")
+    mocker.patch("gi_loadouts.face.rsrc.QFile", MockQFile)
+
+    """
+    This is not a proper implementation. We will change it with runner fixture
+    """
+    test_runner = MainWindow()
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(test_runner.dialog, QMessageBox)
+    assert test_runner.dialog.icon() == QMessageBox.Information
+    assert test_runner.dialog.windowTitle() == "Initialization failed"
+    assert "Training data for Tesseract OCR could not be initialized." in test_runner.dialog.text()
+    assert test_runner.dialog.isVisible()
+
+    """
+    Cleanup the temporary files from temp directory
+    """
+    if path.exists(savefile.name):
+        remove(savefile.name)
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.wind: Remove the already existing training data for Tesseract OCR")
+    ]
+)
+def test_wind_main_fail_continue(runner: MainWindow, mocker: MockerFixture, _: None) -> None:
+    """
+    Test failing removing of training data for Tesseract OCR at startup
+
+    :return:
+    """
+
+    """
+    Mock remove function in rsrc.py for excepting FileNotFoundError
+    """
+    mocker.patch("gi_loadouts.face.rsrc.remove", side_effect=FileNotFoundError)
+    make_temp_file()
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="face.wind: Manually invoke __del__")
+    ]
+)
+def test_wind_del(runner: MainWindow, _: None) -> None:
+    """
+    Test manually invoking __del__
+
+    :return:
+    """
+
+    """
+    pytest-qt does not invoke __del__ automatically.
+    Refer https://github.com/gridhead/gi-loadouts/issues/165
+    for more details. Thus manually invoking __del__ for increasing the coverage.
+    As per ChatGPT: Test runners like pytest-qt keep strong references to objects for logging,
+    reporting, and debugging, which can prevent the automatic invocation of the __del__ method.
+    """
+    runner.__del__()
+
+    """
+    Confirm if the training data for Tesseract OCR is deleted
+    """
+    assert not path.exists(conf.temppath)
diff --git a/test/face/wind/weap/__init__.py b/test/face/wind/weap/__init__.py
new file mode 100644
index 00000000..eda9ef72
--- /dev/null
+++ b/test/face/wind/weap/__init__.py
@@ -0,0 +1,134 @@
+yaml_bow_sample = """
+levl: Level 80/90 (Rank 6)
+name: Skyward Harp
+refn: Refinement 5
+type: bow
+"""
+
+json_bow_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "key": "SkywardHarp",
+  "level": 80,
+  "ascension": 6,
+  "refinement": 5,
+  "type": "bow"
+}
+"""
+
+actual_bow = {
+    "name": "Skyward Harp",
+    "levl": "Level 80/90 (Rank 6)",
+    "refn": "Refinement 5",
+    "type": "Bow"
+}
+
+yaml_catalyst_sample = """
+levl: Level 80/90 (Rank 6)
+name: A Thousand Floating Dreams
+refn: Refinement 5
+type: catalyst
+"""
+
+json_catalyst_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "key": "AThousandFloatingDreams",
+  "level": 80,
+  "ascension": 6,
+  "refinement": 5,
+  "type": "catalyst"
+}
+"""
+
+actual_catalyst = {
+    "name": "A Thousand Floating Dreams",
+    "levl": "Level 80/90 (Rank 6)",
+    "refn": "Refinement 5",
+    "type": "Catalyst"
+}
+
+yaml_claymore_sample = """
+levl: Level 80/90 (Rank 6)
+name: Verdict
+refn: Refinement 5
+type: claymore
+"""
+
+json_claymore_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "key": "Verdict",
+  "level": 80,
+  "ascension": 6,
+  "refinement": 5,
+  "type": "claymore"
+}
+"""
+
+actual_claymore = {
+    "name": "Verdict",
+    "levl": "Level 80/90 (Rank 6)",
+    "refn": "Refinement 5",
+    "type": "Claymore"
+}
+
+yaml_polearm_sample = """
+levl: Level 80/90 (Rank 6)
+name: Engulfing Lightning
+refn: Refinement 5
+type: polearm
+"""
+
+json_polearm_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "key": "EngulfingLightning",
+  "level": 80,
+  "ascension": 6,
+  "refinement": 5,
+  "type": "polearm"
+}
+"""
+
+actual_polearm = {
+    "name": "Engulfing Lightning",
+    "levl": "Level 80/90 (Rank 6)",
+    "refn": "Refinement 5",
+    "type": "Polearm"
+}
+
+yaml_sword_sample = """
+levl: Level 80/90 (Rank 6)
+name: Splendor of Tranquil Waters
+refn: Refinement 5
+type: sword
+"""
+
+json_sword_sample = """
+{
+  "id": "00000000",
+  "location": "",
+  "lock": false,
+  "key": "SplendorOfTranquilWaters",
+  "level": 80,
+  "ascension": 6,
+  "refinement": 5,
+  "type": "sword"
+}
+"""
+
+actual_sword = {
+    "name": "Splendor of Tranquil Waters",
+    "levl": "Level 80/90 (Rank 6)",
+    "refn": "Refinement 5",
+    "type": "Sword"
+}
diff --git a/test/face/wind/weap/kind/__init__.py b/test/face/wind/weap/kind/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/face/wind/weap/kind/test_bow.py b/test/face/wind/weap/kind/test_bow.py
new file mode 100644
index 00000000..547ad0f9
--- /dev/null
+++ b/test/face/wind/weap/kind/test_bow.py
@@ -0,0 +1,87 @@
+import pytest
+
+from gi_loadouts.data.weap.bows import BowsDict
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.weap import Bow, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, levl, batk, seco, valu, refn",
+    [
+        pytest.param("Hunter's Bow", 1, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Bow - Hunter's Bow"),
+        pytest.param("Seasoned Hunter's Bow", 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Bow - Seasoned Hunter's Bow"),
+        pytest.param("Recurve Bow", 3, "Level 80/90 (Rank 6)", 334, WeaponStatType.health_points_perc, 42.7, 5, id="face.wind.rule: Configuring weapon - Bow - Recurve Bow"),
+        pytest.param("Slingshot", 3, "Level 80/90 (Rank 6)", 334, WeaponStatType.critical_rate_perc, 28.5, 5, id="face.wind.rule: Configuring weapon - Bow - Slingshot"),
+        pytest.param("Compound Bow", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.damage_bonus_physical_perc, 62.9, 5, id="face.wind.rule: Configuring weapon - Bow - Compound Bow"),
+        pytest.param("Favonius Warbow", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.energy_recharge_perc, 55.9, 5, id="face.wind.rule: Configuring weapon - Bow - Favonius Warbow"),
+        pytest.param("Hamayumi", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Bow - Hamayumi"),
+        pytest.param("King's Squire", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Bow - King's Squire"),
+        pytest.param("Aqua Simulacra", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, 5, id="face.wind.rule: Configuring weapon - Bow - Aqua Simulacra"),
+        pytest.param("Hunter's Path", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_rate_perc, 40.2, 5, id="face.wind.rule: Configuring weapon - Bow - Hunter's Path"),
+        pytest.param("Silvershower Heartstrings", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.health_points_perc, 60.3, 5, id="face.wind.rule: Configuring weapon - Bow - Silvershower Heartstrings"),
+        pytest.param("Sharpshooter's Oath", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.critical_damage_perc, 42.7, 5, id="face.wind.rule: Configuring weapon - Bow - Sharpshooter's Oath"),
+        pytest.param("Cloudforged", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Bow - Cloudforged"),
+        pytest.param("End of the Line", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, 5, id="face.wind.rule: Configuring weapon - Bow - End of the Line"),
+        pytest.param("Mitternachts Waltz", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.damage_bonus_physical_perc, 47.2, 5, id="face.wind.rule: Configuring weapon - Bow - Mitternachts Waltz"),
+        pytest.param("Predator", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 1, id="face.wind.rule: Configuring weapon - Bow - Predator"),
+        pytest.param("Prototype Crescent", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Bow - Prototype Crescent"),
+        pytest.param("Royal Bow", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Bow - Royal Bow"),
+        pytest.param("Rust", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Bow - Rust"),
+        pytest.param("Song of Stillness", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Bow - Song of Stillness"),
+        pytest.param("The Stringless", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Bow - The Stringless"),
+        pytest.param("The Viridescent Hunt", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Bow - The Viridescent Hunt"),
+        pytest.param("Windblume Ode", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Bow - Windblume Ode"),
+        pytest.param("Amos' Bow", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, 5, id="face.wind.rule: Configuring weapon - Bow - Amos' Bow"),
+        pytest.param("Elegy for the End", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.energy_recharge_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Bow - Elegy for the End"),
+        pytest.param("Polar Star", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, 5, id="face.wind.rule: Configuring weapon - Bow - Polar Star"),
+        pytest.param("The First Great Magic", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_damage_perc, 60.3, 5, id="face.wind.rule: Configuring weapon - Bow - The First Great Magic"),
+        pytest.param("Thundering Pulse", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_damage_perc, 60.3, 5, id="face.wind.rule: Configuring weapon - Bow - Thundering Pulse"),
+        pytest.param("Alley Hunter", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Bow - Alley Hunter"),
+        pytest.param("Blackcliff Warbow", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.critical_damage_perc, 33.5, 5, id="face.wind.rule: Configuring weapon - Bow - Blackcliff Warbow"),
+        pytest.param("Chain Breaker", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Bow - Chain Breaker"),
+        pytest.param("Fading Twilight", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Bow - Fading Twilight"),
+        pytest.param("Ibis Piercer", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Bow - Ibis Piercer"),
+        pytest.param("Mouun's Moon", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Bow - Mouun's Moon"),
+        pytest.param("Range Gauge", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Bow - Range Gauge"),
+        pytest.param("Sacrificial Bow", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Bow - Sacrificial Bow"),
+        pytest.param("Scion of the Blazing Sun", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.critical_rate_perc, 16.8, 5, id="face.wind.rule: Configuring weapon - Bow - Scion of the Blazing Sun"),
+        pytest.param("Skyward Harp", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, 5, id="face.wind.rule: Configuring weapon - Bow - Skyward Harp"),
+    ]
+)
+def test_bow(runner: MainWindow, name: str, rare: int, levl: str, batk: int, seco: WeaponStatType, valu: float, refn: int | None) -> None:
+    """
+    Test configuring weapons of bow type on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_elem.setCurrentText("Anemo")
+    runner.head_char_name.setCurrentText("Venti")
+    unit = BowsDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+    if isinstance(refn, int):
+        runner.weap_area_refn.setCurrentText(f"Refinement {refn}")
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText().strip() == Bow().type.value
+    assert runner.weap_area_rare.text() == " ".join(["STAR"] * unit.rare.value.qant)
+    assert runner.weap_area_batk.text().split(" ")[0] == "ATK"
+    assert verify_accuracy(round(float(runner.weap_area_batk.text().split(" ")[-1])), round(unit.main_stat.stat_data), 1)
+    if unit.seco_stat.stat_name == WeaponStatType.none:
+        assert runner.weap_area_stat.text() == "No substats"
+    else:
+        assert unit.seco_stat.stat_name.value.value in runner.weap_area_stat.text()
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, float(runner.weap_area_stat.text().split(" ")[-1]), 1)
+
+    if isinstance(refn, int):
+        assert runner.weap_area_refn_head.text() == f"<b>{unit.refi_name}</b>"
+        assert runner.weap_area_refn_body.toPlainText() == unit.refi_list[refn - 1]
diff --git a/test/face/wind/weap/kind/test_catalyst.py b/test/face/wind/weap/kind/test_catalyst.py
new file mode 100644
index 00000000..1208dd47
--- /dev/null
+++ b/test/face/wind/weap/kind/test_catalyst.py
@@ -0,0 +1,89 @@
+import pytest
+
+from gi_loadouts.data.weap.catalysts import CatalystsDict
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.weap import Catalyst, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, levl, batk, seco, valu, refn",
+    [
+        pytest.param("Magic Guide", 3, "Level 80/90 (Rank 6)", 334, WeaponStatType.elemental_mastery, 171, 5, id="face.wind.rule: Configuring weapon - Catalyst - Magic Guide"),
+        pytest.param("Dodoco Tales", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Catalyst - Dodoco Tales"),
+        pytest.param("Eye of Perception", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Catalyst - Eye of Perception"),
+        pytest.param("Sacrificial Fragments", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.elemental_mastery, 201, 5, id="face.wind.rule: Configuring weapon - Catalyst - Sacrificial Fragments"),
+        pytest.param("Sacrificial Jade", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.critical_rate_perc, 33.5, 5, id="face.wind.rule: Configuring weapon - Catalyst - Sacrificial Jade"),
+        pytest.param("A Thousand Floating Dreams", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.elemental_mastery, 241, 5, id="face.wind.rule: Configuring weapon - Catalyst - A Thousand Floating Dreams"),
+        pytest.param("Surf's Up", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, 5, id="face.wind.rule: Configuring weapon - Catalyst - Surf's Up"),
+        pytest.param("Tome of the Eternal Flow", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, 5, id="face.wind.rule: Configuring weapon - Catalyst - Tome of the Eternal Flow"),
+        pytest.param("Apprentice's Notes", 1, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Catalyst - Apprentice's Notes"),
+        pytest.param("Pocket Grimoire", 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Catalyst - Pocket Grimoire"),
+        pytest.param("Otherworldly Story", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.energy_recharge_perc, 35.6, 5, id="face.wind.rule: Configuring weapon - Catalyst - Otherworldly Story"),
+        pytest.param("Thrilling Tales of Dragon Slayers", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.health_points_perc, 32.1, 5, id="face.wind.rule: Configuring weapon - Catalyst - Thrilling Tales of Dragon Slayers"),
+        pytest.param("Ash-Graven Drinking Horn", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Catalyst - Ash-Graven Drinking Horn"),
+        pytest.param("Blackcliff Agate", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_damage_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Catalyst - Blackcliff Agate"),
+        pytest.param("Favonius Codex", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, 5, id="face.wind.rule: Configuring weapon - Catalyst - Favonius Codex"),
+        pytest.param("Frostbearer", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Catalyst - Frostbearer"),
+        pytest.param("Fruit of Fulfillment", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, 5, id="face.wind.rule: Configuring weapon - Catalyst - Fruit of Fulfillment"),
+        pytest.param("Prototype Amber", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Catalyst - Prototype Amber"),
+        pytest.param("Ring of Yaxche", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Catalyst - Ring of Yaxche"),
+        pytest.param("Solar Pearl", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Catalyst - Solar Pearl"),
+        pytest.param("The Widsith", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_damage_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Catalyst - The Widsith"),
+        pytest.param("Wandering Evenstar", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Catalyst - Wandering Evenstar"),
+        pytest.param("Everlasting Moonglow", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.health_points_perc, 45.3, 5, id="face.wind.rule: Configuring weapon - Catalyst - Everlasting Moonglow"),
+        pytest.param("Jadefall's Splendor", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.health_points_perc, 45.3, 5, id="face.wind.rule: Configuring weapon - Catalyst - Jadefall's Splendor"),
+        pytest.param("Kagura's Verity", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_damage_perc, 60.3, 5, id="face.wind.rule: Configuring weapon - Catalyst - Kagura's Verity"),
+        pytest.param("Lost Prayer to the Sacred Winds", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, 5, id="face.wind.rule: Configuring weapon - Catalyst - Lost Prayer to the Sacred Winds"),
+        pytest.param("Memory of Dust", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, 5, id="face.wind.rule: Configuring weapon - Catalyst - Memory of Dust"),
+        pytest.param("Emerald Orb", 3, "Level 80/90 (Rank 6)", 415, WeaponStatType.elemental_mastery, 85, 5, id="face.wind.rule: Configuring weapon - Catalyst - Emerald Orb"),
+        pytest.param("Twin Nephrite", 3, "Level 80/90 (Rank 6)", 415, WeaponStatType.critical_rate_perc, 14.2, 5, id="face.wind.rule: Configuring weapon - Catalyst - Twin Nephrite"),
+        pytest.param("Ballad of the Boundless Blue", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Catalyst - Ballad of the Boundless Blue"),
+        pytest.param("Flowing Purity", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Catalyst - Flowing Purity"),
+        pytest.param("Hakushin Ring", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Catalyst - Hakushin Ring"),
+        pytest.param("Mappa Mare", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.elemental_mastery, 101, 5, id="face.wind.rule: Configuring weapon - Catalyst - Mappa Mare"),
+        pytest.param("Oathsworn Eye", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Catalyst - Oathsworn Eye"),
+        pytest.param("Royal Grimoire", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Catalyst - Royal Grimoire"),
+        pytest.param("Wine and Song", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Catalyst - Wine and Song"),
+        pytest.param("Cashflow Supervision", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, 5, id="face.wind.rule: Configuring weapon - Catalyst - Cashflow Supervision"),
+        pytest.param("Skyward Atlas", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.attack_perc, 30.2, 5, id="face.wind.rule: Configuring weapon - Catalyst - Skyward Atlas"),
+        pytest.param("Tulaytullah's Remembrance", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_damage_perc, 40.2, 5, id="face.wind.rule: Configuring weapon - Catalyst - Tulaytullah's Remembrance"),
+        pytest.param("Crane's Echoing Call", 5, "Level 80/90 (Rank 6)", 679, WeaponStatType.attack_perc, 15.1, 5, id="face.wind.rule: Configuring weapon - Catalyst - Crane's Echoing Call"),
+    ]
+)
+def test_catalyst(runner: MainWindow, name: str, rare: int, levl: str, batk: int, seco: WeaponStatType, valu: float, refn: int | None) -> None:
+    """
+    Test configuring weapons of catalyst type on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_elem.setCurrentText("Dendro")
+    runner.head_char_name.setCurrentText("Nahida")
+    unit = CatalystsDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+    if isinstance(refn, int):
+        runner.weap_area_refn.setCurrentText(f"Refinement {refn}")
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText().strip() == Catalyst().type.value
+    assert runner.weap_area_rare.text() == " ".join(["STAR"] * unit.rare.value.qant)
+    assert runner.weap_area_batk.text().split(" ")[0] == "ATK"
+    assert verify_accuracy(round(float(runner.weap_area_batk.text().split(" ")[-1])), round(unit.main_stat.stat_data), 1)
+    if unit.seco_stat.stat_name == WeaponStatType.none:
+        assert runner.weap_area_stat.text() == "No substats"
+    else:
+        assert unit.seco_stat.stat_name.value.value in runner.weap_area_stat.text()
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, float(runner.weap_area_stat.text().split(" ")[-1]), 1)
+
+    if isinstance(refn, int):
+        assert runner.weap_area_refn_head.text() == f"<b>{unit.refi_name}</b>"
+        assert runner.weap_area_refn_body.toPlainText() == unit.refi_list[refn - 1]
diff --git a/test/face/wind/weap/kind/test_claymore.py b/test/face/wind/weap/kind/test_claymore.py
new file mode 100644
index 00000000..f258f0a2
--- /dev/null
+++ b/test/face/wind/weap/kind/test_claymore.py
@@ -0,0 +1,85 @@
+import pytest
+
+from gi_loadouts.data.weap.claymores import ClaymoresDict
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.weap import Claymore, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, levl, batk, seco, valu, refn",
+    [
+        pytest.param("Bloodtainted Greatsword", 3, "Level 80/90 (Rank 6)", 334, WeaponStatType.elemental_mastery, 171, 5, id="face.wind.rule: Configuring weapon - Claymore - Bloodtainted Greatsword"),
+        pytest.param("Portable Power Saw", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.health_points_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Claymore - Portable Power Saw"),
+        pytest.param("Luxurious Sea-Lord", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Claymore - Luxurious Sea-Lord"),
+        pytest.param("Favonius Greatsword", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.energy_recharge_perc, 55.9, 5, id="face.wind.rule: Configuring weapon - Claymore - Favonius Greatsword"),
+        pytest.param("Redhorn Stonethresher", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, 5, id="face.wind.rule: Configuring weapon - Claymore - Redhorn Stonethresher"),
+        pytest.param("Waster Greatsword", 1, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Claymore - Waster Greatsword"),
+        pytest.param("Old Merc's Pal", 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Claymore - Old Merc's Pal"),
+        pytest.param("White Iron Greatsword", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.defense_perc, 40.1, 5, id="face.wind.rule: Configuring weapon - Claymore - White Iron Greatsword"),
+        pytest.param("Skyrider Greatsword", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.damage_bonus_physical_perc, 40.1, 5, id="face.wind.rule: Configuring weapon - Claymore - Skyrider Greatsword"),
+        pytest.param("Ferrous Shadow", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.health_points_perc, 32.1, 5, id="face.wind.rule: Configuring weapon - Claymore - Ferrous Shadow"),
+        pytest.param("Debate Club", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.attack_perc, 32.1, 5, id="face.wind.rule: Configuring weapon - Claymore - Debate Club"),
+        pytest.param("Serpent Spine", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Claymore - Serpent Spine"),
+        pytest.param("Whiteblind", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.defense_perc, 47.2, 5, id="face.wind.rule: Configuring weapon - Claymore - Whiteblind"),
+        pytest.param("Tidal Shadow", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Claymore - Tidal Shadow"),
+        pytest.param("The Bell", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Claymore - The Bell"),
+        pytest.param("Rainslasher", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Claymore - Rainslasher"),
+        pytest.param("Makhaira Aquamarine", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Claymore - Makhaira Aquamarine"),
+        pytest.param("Lithic Blade", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Claymore - Lithic Blade"),
+        pytest.param("Katsuragikiri Nagamasa", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, 5, id="face.wind.rule: Configuring weapon - Claymore - Katsuragikiri Nagamasa"),
+        pytest.param("Blackcliff Slasher", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_damage_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Claymore - Blackcliff Slasher"),
+        pytest.param("Akuoumaru", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Claymore - Akuoumaru"),
+        pytest.param("Beacon of the Reed Sea", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, 5, id="face.wind.rule: Configuring weapon - Claymore - Beacon of the Reed Sea"),
+        pytest.param("Wolf's Gravestone", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, 5, id="face.wind.rule: Configuring weapon - Claymore - Wolf's Gravestone"),
+        pytest.param("The Unforged", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, 5, id="face.wind.rule: Configuring weapon - Claymore - The Unforged"),
+        pytest.param("Talking Stick", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.critical_rate_perc, 16.8, 5, id="face.wind.rule: Configuring weapon - Claymore - Talking Stick"),
+        pytest.param("Snow-Tombed Starsilver", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.damage_bonus_physical_perc, 31.5, 5, id="face.wind.rule: Configuring weapon - Claymore - Snow-Tombed Starsilver"),
+        pytest.param("Royal Greatsword", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Claymore - Royal Greatsword"),
+        pytest.param("Prototype Archaic", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Claymore - Prototype Archaic"),
+        pytest.param("Sacrificial Greatsword", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Claymore - Sacrificial Greatsword"),
+        pytest.param("Mailed Flower", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.elemental_mastery, 101, 5, id="face.wind.rule: Configuring weapon - Claymore - Mailed Flower"),
+        pytest.param("Forest Regalia", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Claymore - Forest Regalia"),
+        pytest.param("Earth Shaker", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Claymore - Earth Shaker"),
+        pytest.param("\"Ultimate Overlord's Mega Magic Sword\"", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Claymore - \"Ultimate Overlord's Mega Magic Sword\""),
+        pytest.param("Skyward Pride", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.energy_recharge_perc, 33.5, 5, id="face.wind.rule: Configuring weapon - Claymore - Skyward Pride"),
+        pytest.param("Verdict", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, 5, id="face.wind.rule: Configuring weapon - Claymore - Verdict"),
+        pytest.param("Song of Broken Pines", 5, "Level 80/90 (Rank 6)", 679, WeaponStatType.damage_bonus_physical_perc, 18.9, 5, id="face.wind.rule: Configuring weapon - Claymore - Song of Broken Pines"),
+    ]
+)
+def test_claymore(runner: MainWindow, name: str, rare: int, levl: str, batk: int, seco: WeaponStatType, valu: float, refn: int | None) -> None:
+    """
+    Test configuring weapons of claymore type on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_elem.setCurrentText("Geo")
+    runner.head_char_name.setCurrentText("Navia")
+    unit = ClaymoresDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+    if isinstance(refn, int):
+        runner.weap_area_refn.setCurrentText(f"Refinement {refn}")
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText().strip() == Claymore().type.value
+    assert runner.weap_area_rare.text() == " ".join(["STAR"] * unit.rare.value.qant)
+    assert runner.weap_area_batk.text().split(" ")[0] == "ATK"
+    assert verify_accuracy(round(float(runner.weap_area_batk.text().split(" ")[-1])), round(unit.main_stat.stat_data), 1)
+    if unit.seco_stat.stat_name == WeaponStatType.none:
+        assert runner.weap_area_stat.text() == "No substats"
+    else:
+        assert unit.seco_stat.stat_name.value.value in runner.weap_area_stat.text()
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, float(runner.weap_area_stat.text().split(" ")[-1]), 1)
+
+    if isinstance(refn, int):
+        assert runner.weap_area_refn_head.text() == f"<b>{unit.refi_name}</b>"
+        assert runner.weap_area_refn_body.toPlainText() == unit.refi_list[refn - 1]
diff --git a/test/face/wind/weap/kind/test_polearm.py b/test/face/wind/weap/kind/test_polearm.py
new file mode 100644
index 00000000..75122578
--- /dev/null
+++ b/test/face/wind/weap/kind/test_polearm.py
@@ -0,0 +1,82 @@
+import pytest
+
+from gi_loadouts.data.weap.polearms import PolearmsDict
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.weap import Polearm, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, levl, batk, seco, valu, refn",
+    [
+        pytest.param("Beginner's Protector", 1, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Polearm - Beginner's Protector"),
+        pytest.param("Iron Point", 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Polearm - Iron Point"),
+        pytest.param("Black Tassel", 3, "Level 80/90 (Rank 6)", 334, WeaponStatType.health_points_perc, 42.7, 5, id="face.wind.rule: Configuring weapon - Polearm - Black Tassel"),
+        pytest.param("Deathmatch", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.critical_rate_perc, 33.5, 5, id="face.wind.rule: Configuring weapon - Polearm - Deathmatch"),
+        pytest.param("Dragon's Bane", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.elemental_mastery, 201, 5, id="face.wind.rule: Configuring weapon - Polearm - Dragon's Bane"),
+        pytest.param("Dragonspine Spear", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.damage_bonus_physical_perc, 62.9, 5, id="face.wind.rule: Configuring weapon - Polearm - Dragonspine Spear"),
+        pytest.param("Staff of the Scarlet Sands", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_rate_perc, 40.2, 5, id="face.wind.rule: Configuring weapon - Polearm - Staff of the Scarlet Sands"),
+        pytest.param("White Tassel", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.critical_rate_perc, 21.4, 5, id="face.wind.rule: Configuring weapon - Polearm - White Tassel"),
+        pytest.param("\"The Catch\"", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, 5, id="face.wind.rule: Configuring weapon - Polearm - \"The Catch\""),
+        pytest.param("Ballad of the Fjords", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Polearm - Ballad of the Fjords"),
+        pytest.param("Blackcliff Pole", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_damage_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Polearm - Blackcliff Pole"),
+        pytest.param("Dialogues of the Desert Sages", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Polearm - Dialogues of the Desert Sages"),
+        pytest.param("Footprint of the Rainbow", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.defense_perc, 47.2, 5, id="face.wind.rule: Configuring weapon - Polearm - Footprint of the Rainbow"),
+        pytest.param("Missive Windspear", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Polearm - Missive Windspear"),
+        pytest.param("Prototype Starglitter", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, 5, id="face.wind.rule: Configuring weapon - Polearm - Prototype Starglitter"),
+        pytest.param("Engulfing Lightning", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.energy_recharge_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Polearm - Engulfing Lightning"),
+        pytest.param("Lumidouce Elegy", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, 5, id="face.wind.rule: Configuring weapon - Polearm - Lumidouce Elegy"),
+        pytest.param("Staff of Homa", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_damage_perc, 60.3, 5, id="face.wind.rule: Configuring weapon - Polearm - Staff of Homa"),
+        pytest.param("Vortex Vanquisher", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, 5, id="face.wind.rule: Configuring weapon - Polearm - Vortex Vanquisher"),
+        pytest.param("Halberd", 3, "Level 80/90 (Rank 6)", 415, WeaponStatType.attack_perc, 21.4, 5, id="face.wind.rule: Configuring weapon - Polearm - Halberd"),
+        pytest.param("Crescent Pike", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.damage_bonus_physical_perc, 31.5, 5, id="face.wind.rule: Configuring weapon - Polearm - Crescent Pike"),
+        pytest.param("Favonius Lance", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Polearm - Favonius Lance"),
+        pytest.param("Kitain Cross Spear", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.elemental_mastery, 101, 5, id="face.wind.rule: Configuring weapon - Polearm - Kitain Cross Spear"),
+        pytest.param("Lithic Spear", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Polearm - Lithic Spear"),
+        pytest.param("Moonpiercer", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.elemental_mastery, 101, 5, id="face.wind.rule: Configuring weapon - Polearm - Moonpiercer"),
+        pytest.param("Prospector's Drill", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Polearm - Prospector's Drill"),
+        pytest.param("Rightful Reward", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.health_points_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Polearm - Rightful Reward"),
+        pytest.param("Royal Spear", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Polearm - Royal Spear"),
+        pytest.param("Crimson Moon's Semblance", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, 5, id="face.wind.rule: Configuring weapon - Polearm - Crimson Moon's Semblance"),
+        pytest.param("Primordial Jade Winged-Spear", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_rate_perc, 20.1, 5, id="face.wind.rule: Configuring weapon - Polearm - Primordial Jade Winged-Spear"),
+        pytest.param("Skyward Spine", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.energy_recharge_perc, 33.5, 5, id="face.wind.rule: Configuring weapon - Polearm - Skyward Spine"),
+        pytest.param("Wavebreaker's Fin", 4, "Level 80/90 (Rank 6)", 571, WeaponStatType.attack_perc, 12.5, 5, id="face.wind.rule: Configuring weapon - Polearm - Wavebreaker's Fin"),
+        pytest.param("Calamity Queller", 5, "Level 80/90 (Rank 6)", 679, WeaponStatType.attack_perc, 15.1, 5, id="face.wind.rule: Configuring weapon - Polearm - Calamity Queller"),
+    ]
+)
+def test_polearm(runner: MainWindow, name: str, rare: int, levl: str, batk: int, seco: WeaponStatType, valu: float, refn: int | None) -> None:
+    """
+    Test configuring weapons of polearm type on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_elem.setCurrentText("Electro")
+    runner.head_char_name.setCurrentText("Raiden Shogun")
+    unit = PolearmsDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+    if isinstance(refn, int):
+        runner.weap_area_refn.setCurrentText(f"Refinement {refn}")
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText().strip() == Polearm().type.value
+    assert runner.weap_area_rare.text() == " ".join(["STAR"] * unit.rare.value.qant)
+    assert runner.weap_area_batk.text().split(" ")[0] == "ATK"
+    assert verify_accuracy(round(float(runner.weap_area_batk.text().split(" ")[-1])), round(unit.main_stat.stat_data), 1)
+    if unit.seco_stat.stat_name == WeaponStatType.none:
+        assert runner.weap_area_stat.text() == "No substats"
+    else:
+        assert unit.seco_stat.stat_name.value.value in runner.weap_area_stat.text()
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, float(runner.weap_area_stat.text().split(" ")[-1]), 1)
+
+    if isinstance(refn, int):
+        assert runner.weap_area_refn_head.text() == f"<b>{unit.refi_name}</b>"
+        assert runner.weap_area_refn_body.toPlainText() == unit.refi_list[refn - 1]
diff --git a/test/face/wind/weap/kind/test_sword.py b/test/face/wind/weap/kind/test_sword.py
new file mode 100644
index 00000000..1232952a
--- /dev/null
+++ b/test/face/wind/weap/kind/test_sword.py
@@ -0,0 +1,93 @@
+import pytest
+
+from gi_loadouts.data.weap.swords import SwordsDict
+from gi_loadouts.face.wind.main import MainWindow
+from gi_loadouts.type.levl import Level
+from gi_loadouts.type.weap import Sword, WeaponStatType
+from test import verify_accuracy
+
+
+@pytest.mark.parametrize(
+    "name, rare, levl, batk, seco, valu, refn",
+    [
+        pytest.param("Skyrider Sword", 3, "Level 80/90 (Rank 6)", 334, WeaponStatType.energy_recharge_perc, 47.2, 5, id="face.wind.rule: Configuring weapon - Sword - Skyrider Sword"),
+        pytest.param("Amenoma Kageuchi", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.attack_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Sword - Amenoma Kageuchi"),
+        pytest.param("Cinnabar Spindle", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.defense_perc, 62.9, 5, id="face.wind.rule: Configuring weapon - Sword - Cinnabar Spindle"),
+        pytest.param("Favonius Sword", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.energy_recharge_perc, 55.9, 5, id="face.wind.rule: Configuring weapon - Sword - Favonius Sword"),
+        pytest.param("Flute of Ezpitzal", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.defense_perc, 62.9, 5, id="face.wind.rule: Configuring weapon - Sword - Flute of Ezpitzal"),
+        pytest.param("Sacrificial Sword", 4, "Level 80/90 (Rank 6)", 427, WeaponStatType.energy_recharge_perc, 55.9, 5, id="face.wind.rule: Configuring weapon - Sword - Sacrificial Sword"),
+        pytest.param("Key of Khaj-Nisut", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.health_points_perc, 60.3, 5, id="face.wind.rule: Configuring weapon - Sword - Key of Khaj-Nisut"),
+        pytest.param("Light of Foliar Incision", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, 5, id="face.wind.rule: Configuring weapon - Sword - Light of Foliar Incision"),
+        pytest.param("Primordial Jade Cutter", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_rate_perc, 40.2, 5, id="face.wind.rule: Configuring weapon - Sword - Primordial Jade Cutter"),
+        pytest.param("Splendor of Tranquil Waters", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, 5, id="face.wind.rule: Configuring weapon - Sword - Splendor of Tranquil Waters"),
+        pytest.param("Uraku Misugiri", 5, "Level 80/90 (Rank 6)", 506, WeaponStatType.critical_damage_perc, 80.4, 5, id="face.wind.rule: Configuring weapon - Sword - Uraku Misugiri"),
+        pytest.param("Dull Blade", 1, "Level 60/70 (Rank 4)", 169, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Sword - Dull Blade"),
+        pytest.param("Silver Sword", 2, "Level 60/70 (Rank 4)", 220, WeaponStatType.none, 0, None, id="face.wind.rule: Configuring weapon - Sword - Silver Sword"),
+        pytest.param("Cool Steel", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.attack_perc, 32.1, 5, id="face.wind.rule: Configuring weapon - Sword - Cool Steel"),
+        pytest.param("Dark Iron Sword", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.elemental_mastery, 128, 5, id="face.wind.rule: Configuring weapon - Sword - Dark Iron Sword"),
+        pytest.param("Fillet Blade", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.attack_perc, 32.1, 5, id="face.wind.rule: Configuring weapon - Sword - Fillet Blade"),
+        pytest.param("Harbinger of Dawn", 3, "Level 80/90 (Rank 6)", 375, WeaponStatType.critical_damage_perc, 42.7, 5, id="face.wind.rule: Configuring weapon - Sword - Harbinger of Dawn"),
+        pytest.param("Festering Desire", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, 5, id="face.wind.rule: Configuring weapon - Sword - Festering Desire"),
+        pytest.param("Fleuve Cendre Ferryman", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.energy_recharge_perc, 41.9, 5, id="face.wind.rule: Configuring weapon - Sword - Fleuve Cendre Ferryman"),
+        pytest.param("Iron Sting", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Sword - Iron Sting"),
+        pytest.param("Kagotsurube Isshin", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 1, id="face.wind.rule: Configuring weapon - Sword - Kagotsurube Isshin"),
+        pytest.param("Lion's Roar", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Sword - Lion's Roar"),
+        pytest.param("Royal Longsword", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Sword - Royal Longsword"),
+        pytest.param("Sword of Narzissenkreuz", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Sword - Sword of Narzissenkreuz"),
+        pytest.param("The Black Sword", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Sword - The Black Sword"),
+        pytest.param("The Dockhand's Assistant", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.health_points_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Sword - The Dockhand's Assistant"),
+        pytest.param("The Flute", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.attack_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Sword - The Flute"),
+        pytest.param("Toukabou Shigure", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Sword - Toukabou Shigure"),
+        pytest.param("Wolf-Fang", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.critical_rate_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Sword - Wolf-Fang"),
+        pytest.param("Xiphos' Moonlight", 4, "Level 80/90 (Rank 6)", 475, WeaponStatType.elemental_mastery, 151, 5, id="face.wind.rule: Configuring weapon - Sword - Xiphos' Moonlight"),
+        pytest.param("Freedom-Sworn", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.elemental_mastery, 181, 5, id="face.wind.rule: Configuring weapon - Sword - Freedom-Sworn"),
+        pytest.param("Haran Geppaku Futsu", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.critical_rate_perc, 30.2, 5, id="face.wind.rule: Configuring weapon - Sword - Haran Geppaku Futsu"),
+        pytest.param("Skyward Blade", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.energy_recharge_perc, 50.3, 5, id="face.wind.rule: Configuring weapon - Sword - Skyward Blade"),
+        pytest.param("Summit Shaper", 5, "Level 80/90 (Rank 6)", 563, WeaponStatType.attack_perc, 45.3, 5, id="face.wind.rule: Configuring weapon - Sword - Summit Shaper"),
+        pytest.param("Traveler's Handy Sword", 3, "Level 80/90 (Rank 6)", 415, WeaponStatType.defense_perc, 26.7, 5, id="face.wind.rule: Configuring weapon - Sword - Traveler's Handy Sword"),
+        pytest.param("Blackcliff Longsword", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.critical_damage_perc, 33.5, 5, id="face.wind.rule: Configuring weapon - Sword - Blackcliff Longsword"),
+        pytest.param("Finale of the Deep", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.attack_perc, 25.1, 5, id="face.wind.rule: Configuring weapon - Sword - Finale of the Deep"),
+        pytest.param("Prototype Rancour", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.damage_bonus_physical_perc, 31.5, 5, id="face.wind.rule: Configuring weapon - Sword - Prototype Rancour"),
+        pytest.param("Sapwood Blade", 4, "Level 80/90 (Rank 6)", 523, WeaponStatType.energy_recharge_perc, 27.9, 5, id="face.wind.rule: Configuring weapon - Sword - Sapwood Blade"),
+        pytest.param("Absolution", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_damage_perc, 40.2, 5, id="face.wind.rule: Configuring weapon - Sword - Absolution"),
+        pytest.param("Aquila Favonia", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.damage_bonus_physical_perc, 37.7, 5, id="face.wind.rule: Configuring weapon - Sword - Aquila Favonia"),
+        pytest.param("Mistsplitter Reforged", 5, "Level 80/90 (Rank 6)", 621, WeaponStatType.critical_damage_perc, 40.2, 5, id="face.wind.rule: Configuring weapon - Sword - Mistsplitter Reforged"),
+        pytest.param("The Alley Flash", 4, "Level 80/90 (Rank 6)", 571, WeaponStatType.elemental_mastery, 50, 5, id="face.wind.rule: Configuring weapon - Sword - The Alley Flash"),
+        #TODO pytest.param("Sword of Descension", 0, "Level 80/90 (Rank 6)", 414, WeaponStatType.attack_perc, 32.1, 5, id="face.wind.rule: Configuring weapon - Sword - Sword of Descension"),
+    ]
+)
+def test_sword(runner: MainWindow, name: str, rare: int, levl: str, batk: int, seco: WeaponStatType, valu: float, refn: int | None) -> None:
+    """
+    Test configuring weapons of sword type on the user interface
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_elem.setCurrentText("Hydro")
+    runner.head_char_name.setCurrentText("Furina")
+    unit = SwordsDict[name]()
+    unit.levl = getattr(Level, levl.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"))
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+    if isinstance(refn, int):
+        runner.weap_area_refn.setCurrentText(f"Refinement {refn}")
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText().strip() == Sword().type.value
+    assert runner.weap_area_rare.text() == " ".join(["STAR"] * unit.rare.value.qant)
+    assert runner.weap_area_batk.text().split(" ")[0] == "ATK"
+    assert verify_accuracy(round(float(runner.weap_area_batk.text().split(" ")[-1])), round(unit.main_stat.stat_data), 1)
+    if unit.seco_stat.stat_name == WeaponStatType.none:
+        assert runner.weap_area_stat.text() == "No substats"
+    else:
+        assert unit.seco_stat.stat_name.value.value in runner.weap_area_stat.text()
+        assert verify_accuracy(unit.seco_stat_calc.stat_data, float(runner.weap_area_stat.text().split(" ")[-1]), 1)
+
+    if isinstance(refn, int):
+        assert runner.weap_area_refn_head.text() == f"<b>{unit.refi_name}</b>"
+        assert runner.weap_area_refn_body.toPlainText() == unit.refi_list[refn - 1]
diff --git a/test/face/wind/weap/test_file.py b/test/face/wind/weap/test_file.py
new file mode 100644
index 00000000..5467cbbc
--- /dev/null
+++ b/test/face/wind/weap/test_file.py
@@ -0,0 +1,664 @@
+from os import path, remove
+from pathlib import Path
+from random import choice
+from tempfile import NamedTemporaryFile, gettempdir
+from uuid import uuid4
+
+import pytest
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QFileDialog, QMessageBox
+from pytest_mock import MockerFixture
+from pytestqt.qtbot import QtBot
+
+from gi_loadouts.data.weap import Family
+from gi_loadouts.face.wind import file
+from gi_loadouts.face.wind.main import MainWindow
+from test import json_type, yaml_type
+
+from . import (
+    actual_bow,
+    actual_catalyst,
+    actual_claymore,
+    actual_polearm,
+    actual_sword,
+    json_bow_sample,
+    json_catalyst_sample,
+    json_claymore_sample,
+    json_polearm_sample,
+    json_sword_sample,
+    yaml_bow_sample,
+    yaml_catalyst_sample,
+    yaml_claymore_sample,
+    yaml_polearm_sample,
+    yaml_sword_sample,
+)
+
+
+@pytest.mark.parametrize(
+    "type, char",
+    [
+        pytest.param("Bow", "Venti", id="face.wind.rule: Saving a random weapon of bow type"),
+        pytest.param("Catalyst", "Nahida", id="face.wind.rule: Saving a random weapon of catalyst type"),
+        pytest.param("Claymore", "Navia", id="face.wind.rule: Saving a random weapon of claymore type"),
+        pytest.param("Polearm", "Raiden Shogun", id="face.wind.rule: Saving a random weapon of polearm type"),
+        pytest.param("Sword", "Furina", id="face.wind.rule: Saving a random weapon of sword type"),
+    ]
+)
+def test_weap_save(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, type: str, char: str) -> None:
+    """
+    Test saving a random weapon of a certain type
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    name = choice(list(Family[type].keys()))
+    objc = Family[type][name]()
+    levl = choice(objc.levl_bind).value.name
+
+    runner.head_char_name.setCurrentText(char)
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    mocker.patch.object(file.FileHandling, "save", return_value=True)
+    qtbot.mouseClick(runner.weap_head_save, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText() == type
+    assert runner.statarea.currentMessage() == "Weapon data has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "type, char",
+    [
+        pytest.param("Bow", "Venti", id="face.wind.rule: Failing to save a random weapon of bow type"),
+        pytest.param("Catalyst", "Nahida", id="face.wind.rule: Failing to save a random weapon of catalyst type"),
+        pytest.param("Claymore", "Navia", id="face.wind.rule: Failing to save a random weapon of claymore type"),
+        pytest.param("Polearm", "Raiden Shogun", id="face.wind.rule: Failing to save a random weapon of polearm type"),
+        pytest.param("Sword", "Furina", id="face.wind.rule: Failing to save a random weapon of sword type"),
+    ]
+)
+def test_weap_save_fail(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, type: str, char: str) -> None:
+    """
+    Test failing to save a random weapon of a certain type
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    name = choice(list(Family[type].keys()))
+    objc = Family[type][name]()
+    levl = choice(objc.levl_bind).value.name
+
+    runner.head_char_name.setCurrentText(char)
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    mocker.patch.object(file.FileHandling, "save", side_effect=ValueError("Mocked mismatch."))
+    qtbot.mouseClick(runner.weap_head_save, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText() == type
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Save failed"
+    assert "Please confirm that the location that is accessible before saving the weapon data." in runner.dialog.text()
+    assert "Mocked mismatch." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "char, sample, data",
+    [
+        pytest.param("Venti", yaml_bow_sample, actual_bow, id="face.wind.rule: Loading a weapon of bow type from YAML data"),
+        pytest.param("Nahida", yaml_catalyst_sample, actual_catalyst, id="face.wind.rule: Loading a weapon of catalyst type from YAML data"),
+        pytest.param("Navia", yaml_claymore_sample, actual_claymore, id="face.wind.rule: Loading a weapon of claymore type from YAML data"),
+        pytest.param("Raiden Shogun", yaml_polearm_sample, actual_polearm, id="face.wind.rule: Loading a weapon of polearm type from YAML data"),
+        pytest.param("Furina", yaml_sword_sample, actual_sword, id="face.wind.rule: Loading a weapon of sword type from YAML data"),
+    ]
+)
+def test_weap_load_yaml(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, sample: str, data: dict) -> None:
+    """
+    Test loading a weapon of a certain type from YAML data
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, yaml_type))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText() == data["type"]
+    assert runner.weap_area_name.currentText() == data["name"]
+    assert runner.weap_area_refn.currentText() == data["refn"]
+    assert runner.weap_area_levl.currentText() == data["levl"]
+
+
+@pytest.mark.parametrize(
+    "char, sample, data",
+    [
+        pytest.param("Venti", json_bow_sample, actual_bow, id="face.wind.rule: Loading a weapon of bow type from JSON data"),
+        pytest.param("Nahida", json_catalyst_sample, actual_catalyst, id="face.wind.rule: Loading a weapon of catalyst type from JSON data"),
+        pytest.param("Navia", json_claymore_sample, actual_claymore, id="face.wind.rule: Loading a weapon of claymore type from JSON data"),
+        pytest.param("Raiden Shogun", json_polearm_sample, actual_polearm, id="face.wind.rule: Loading a weapon of polearm type from JSON data"),
+        pytest.param("Furina", json_sword_sample, actual_sword, id="face.wind.rule: Loading a weapon of sword type from JSON data"),
+    ]
+)
+def test_weap_load_json(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, sample: str, data: dict) -> None:
+    """
+    Test loading a weapon of a certain type from JSON data
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, json_type))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_type.currentText() == data["type"]
+    assert runner.weap_area_name.currentText() == data["name"]
+    assert runner.weap_area_refn.currentText() == data["refn"]
+    assert runner.weap_area_levl.currentText() == data["levl"]
+
+
+@pytest.mark.parametrize(
+    "char, name",
+    [
+        pytest.param("Venti", "Alley Hunter", id="face.wind.rule: Cancelling loading a weapon of bow type from YAML data"),
+        pytest.param("Nahida", "Apprentice's Notes", id="face.wind.rule: Cancelling loading a weapon of catalyst type from YAML data"),
+        pytest.param("Navia", "Akuoumaru", id="face.wind.rule: Cancelling loading a weapon of claymore type from YAML data"),
+        pytest.param("Raiden Shogun", "Ballad of the Fjords", id="face.wind.rule: Cancelling loading a weapon of polearm type from YAML data"),
+        pytest.param("Furina", "Absolution", id="face.wind.rule: Cancelling loading a weapon of sword type from YAML data"),
+    ]
+)
+def test_weap_load_nope(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, name: str) -> None:
+    """
+    Test cancelling loading a weapon of a certain type
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(False, "", ""))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.weap_area_name.currentText() == name
+
+
+@pytest.mark.parametrize(
+    "char",
+    [
+        pytest.param("Venti", id="face.wind.rule: Loading a weapon of bow type from an empty file"),
+        pytest.param("Nahida", id="face.wind.rule: Loading a weapon of catalyst type from an empty file"),
+        pytest.param("Navia", id="face.wind.rule: Loading a weapon of claymore type from an empty file"),
+        pytest.param("Raiden Shogun", id="face.wind.rule: Loading a weapon of polearm type from an empty file"),
+        pytest.param("Furina", id="face.wind.rule: Loading a weapon of sword type from an empty file"),
+    ]
+)
+def test_team_load_void(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str) -> None:
+    """
+    Test loading a weapon of a certain type from an empty file
+
+    :return:
+    """
+
+    """
+    Perform the action of saving the weapon information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, "", ""))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the location that is accessible before loading the weapon data." in runner.dialog.text()
+    assert "Selected file cannot be read." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "char, sample, type",
+    [
+        pytest.param("Nahida", yaml_bow_sample, yaml_type, id="face.wind.rule: Failing to load a weapon of bow type from YAML data due to type mismatch"),
+        pytest.param("Navia", yaml_catalyst_sample, yaml_type, id="face.wind.rule: Failing to load a weapon of catalyst type from YAML data due to type mismatch"),
+        pytest.param("Raiden Shogun", yaml_claymore_sample, yaml_type, id="face.wind.rule: Failing to load a weapon of claymore type from YAML data due to type mismatch"),
+        pytest.param("Furina", yaml_polearm_sample, yaml_type, id="face.wind.rule: Failing to load a weapon of polearm type from YAML data due to type mismatch"),
+        pytest.param("Venti", yaml_sword_sample, yaml_type, id="face.wind.rule: Failing to load a weapon of sword type from YAML data due to type mismatch"),
+        pytest.param("Nahida", json_bow_sample, json_type, id="face.wind.rule: Failing to load a weapon of bow type from JSON data due to type mismatch"),
+        pytest.param("Navia", json_catalyst_sample, json_type, id="face.wind.rule: Failing to load a weapon of catalyst type from JSON data due to type mismatch"),
+        pytest.param("Raiden Shogun", json_claymore_sample, json_type, id="face.wind.rule: Failing to load a weapon of claymore type from JSON data due to type mismatch"),
+        pytest.param("Furina", json_polearm_sample, json_type, id="face.wind.rule: Failing to load a weapon of polearm type from JSON data due to type mismatch"),
+        pytest.param("Venti", json_sword_sample, json_type, id="face.wind.rule: Failing to load a weapon of sword type from JSON data due to type mismatch"),
+    ]
+)
+def test_weap_load_fail_type(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, sample: str, type: str) -> None:
+    """
+    Test failing to load a weapon of a certain type from data due to type mismatch
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, type))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the location that is accessible before loading the weapon data." in runner.dialog.text()
+    assert "Weapon type cannot be identified." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "char, sample, type, data",
+    [
+        pytest.param("Venti", yaml_bow_sample, yaml_type, actual_bow, id="face.wind.rule: Failing to load a weapon of bow type from YAML data due to incorrect name"),
+        pytest.param("Nahida", yaml_catalyst_sample, yaml_type, actual_catalyst, id="face.wind.rule: Failing to load a weapon of catalyst type from YAML data due to incorrect name"),
+        pytest.param("Navia", yaml_claymore_sample, yaml_type, actual_claymore, id="face.wind.rule: Failing to load a weapon of claymore type from YAML data due to incorrect name"),
+        pytest.param("Raiden Shogun", yaml_polearm_sample, yaml_type, actual_polearm, id="face.wind.rule: Failing to load a weapon of polearm type from YAML data due to incorrect name"),
+        pytest.param("Furina", yaml_sword_sample, yaml_type, actual_sword, id="face.wind.rule: Failing to load a weapon of sword type from YAML data due to incorrect name"),
+        pytest.param("Venti", json_bow_sample, json_type, actual_bow, id="face.wind.rule: Failing to load a weapon of bow type from JSON data due to incorrect name"),
+        pytest.param("Nahida", json_catalyst_sample, json_type, actual_catalyst, id="face.wind.rule: Failing to load a weapon of catalyst type from JSON data due to incorrect name"),
+        pytest.param("Navia", json_claymore_sample, json_type, actual_claymore, id="face.wind.rule: Failing to load a weapon of claymore type from JSON data due to incorrect name"),
+        pytest.param("Raiden Shogun", json_polearm_sample, json_type, actual_polearm, id="face.wind.rule: Failing to load a weapon of polearm type from JSON data due to incorrect name"),
+        pytest.param("Furina", json_sword_sample, json_type, actual_sword, id="face.wind.rule: Failing to load a weapon of sword type from JSON data due to incorrect name"),
+    ]
+)
+def test_weap_load_fail_name(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, sample: str, type: str, data: dict) -> None:
+    """
+    Test failing to load a weapon of a certain type from data due to incorrect name
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    if type == yaml_type:
+        name = data["name"]
+    else:
+        name = "".join([word.title() for word in data["name"].replace("'", "").replace("\"", "").replace("-", " ").split(" ")])
+
+    sample = sample.replace(name, "AWRY")
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, type))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the location that is accessible before loading the weapon data." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+    if type == yaml_type:
+        assert "Weapon name cannot be identified." in runner.dialog.text()
+    else:
+        assert "Weapon data cannot be parsed." in runner.dialog.text()
+
+
+@pytest.mark.parametrize(
+    "char, sample, type, data",
+    [
+        pytest.param("Venti", yaml_bow_sample, yaml_type, actual_bow, id="face.wind.rule: Failing to load a weapon of bow type from YAML data due to incorrect level"),
+        pytest.param("Nahida", yaml_catalyst_sample, yaml_type, actual_catalyst, id="face.wind.rule: Failing to load a weapon of catalyst type from YAML data due to incorrect level"),
+        pytest.param("Navia", yaml_claymore_sample, yaml_type, actual_claymore, id="face.wind.rule: Failing to load a weapon of claymore type from YAML data due to incorrect level"),
+        pytest.param("Raiden Shogun", yaml_polearm_sample, yaml_type, actual_polearm, id="face.wind.rule: Failing to load a weapon of polearm type from YAML data due to incorrect level"),
+        pytest.param("Furina", yaml_sword_sample, yaml_type, actual_sword, id="face.wind.rule: Failing to load a weapon of sword type from YAML data due to incorrect level"),
+        pytest.param("Venti", json_bow_sample, json_type, actual_bow, id="face.wind.rule: Failing to load a weapon of bow type from JSON data due to incorrect level"),
+        pytest.param("Nahida", json_catalyst_sample, json_type, actual_catalyst, id="face.wind.rule: Failing to load a weapon of catalyst type from JSON data due to incorrect level"),
+        pytest.param("Navia", json_claymore_sample, json_type, actual_claymore, id="face.wind.rule: Failing to load a weapon of claymore type from JSON data due to incorrect level"),
+        pytest.param("Raiden Shogun", json_polearm_sample, json_type, actual_polearm, id="face.wind.rule: Failing to load a weapon of polearm type from JSON data due to incorrect level"),
+        pytest.param("Furina", json_sword_sample, json_type, actual_sword, id="face.wind.rule: Failing to load a weapon of sword type from JSON data due to incorrect level"),
+    ]
+)
+def test_weap_load_fail_levl(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, sample: str, type: str, data: dict) -> None:
+    """
+    Test failing to load a weapon of a certain type from data due to incorrect level
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    sample = sample.replace("80", "100")
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, type))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the location that is accessible before loading the weapon data." in runner.dialog.text()
+    assert "Weapon data cannot be parsed." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "char, sample, type, data",
+    [
+        pytest.param("Venti", yaml_bow_sample, yaml_type, actual_bow, id="face.wind.rule: Failing to load a weapon of bow type from YAML data due to incorrect refinement"),
+        pytest.param("Nahida", yaml_catalyst_sample, yaml_type, actual_catalyst, id="face.wind.rule: Failing to load a weapon of catalyst type from YAML data due to incorrect refinement"),
+        pytest.param("Navia", yaml_claymore_sample, yaml_type, actual_claymore, id="face.wind.rule: Failing to load a weapon of claymore type from YAML data due to incorrect refinement"),
+        pytest.param("Raiden Shogun", yaml_polearm_sample, yaml_type, actual_polearm, id="face.wind.rule: Failing to load a weapon of polearm type from YAML data due to incorrect refinement"),
+        pytest.param("Furina", yaml_sword_sample, yaml_type, actual_sword, id="face.wind.rule: Failing to load a weapon of sword type from YAML data due to incorrect refinement"),
+        pytest.param("Venti", json_bow_sample, json_type, actual_bow, id="face.wind.rule: Failing to load a weapon of bow type from JSON data due to incorrect refinement"),
+        pytest.param("Nahida", json_catalyst_sample, json_type, actual_catalyst, id="face.wind.rule: Failing to load a weapon of catalyst type from JSON data due to incorrect refinement"),
+        pytest.param("Navia", json_claymore_sample, json_type, actual_claymore, id="face.wind.rule: Failing to load a weapon of claymore type from JSON data due to incorrect refinement"),
+        pytest.param("Raiden Shogun", json_polearm_sample, json_type, actual_polearm, id="face.wind.rule: Failing to load a weapon of polearm type from JSON data due to incorrect refinement"),
+        pytest.param("Furina", json_sword_sample, json_type, actual_sword, id="face.wind.rule: Failing to load a weapon of sword type from JSON data due to incorrect refinement"),
+    ]
+)
+def test_weap_load_fail_refn(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, sample: str, type: str, data: dict) -> None:
+    """
+    Test failing to load a weapon of a certain type from data due to incorrect refinement
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    if type == yaml_type:
+        sample = sample.replace("refn: Refinement 5", "refn: Refinement 0")
+    else:
+        sample = sample.replace("\"refinement\": 5,", "\"refinement\": 0,")
+    mocker.patch.object(file.FileHandling, "load", return_value=(True, sample, type))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert isinstance(runner.dialog, QMessageBox)
+    assert runner.dialog.icon() == QMessageBox.Information
+    assert runner.dialog.windowTitle() == "Load failed"
+    assert "Please confirm that the location that is accessible before loading the weapon data." in runner.dialog.text()
+    assert "Weapon refinement cannot be parsed." in runner.dialog.text()
+    assert runner.dialog.isVisible()
+
+
+@pytest.mark.parametrize(
+    "type, char",
+    [
+        pytest.param("Bow", "Venti", id="face.wind.rule: Saving a random weapon of bow type actually as a YAML file"),
+        pytest.param("Catalyst", "Nahida", id="face.wind.rule: Saving a random weapon of catalyst type actually as a YAML file"),
+        pytest.param("Claymore", "Navia", id="face.wind.rule: Saving a random weapon of claymore type actually as a YAML file"),
+        pytest.param("Polearm", "Raiden Shogun", id="face.wind.rule: Saving a random weapon of polearm type actually as a YAML file"),
+        pytest.param("Sword", "Furina", id="face.wind.rule: Saving a random weapon of sword type actually as a YAML file"),
+    ]
+)
+def test_weap_save_yaml_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, type: str, char: str) -> None:
+    """
+    Test saving a random weapon of a certain type as a YAML file
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    name = choice(list(Family[type].keys()))
+    objc = Family[type][name]()
+    levl = choice(objc.levl_bind).value.name
+
+    runner.head_char_name.setCurrentText(char)
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+
+    """
+    Perform the action of saving the weapon information
+    """
+    extn = ".yaml"
+
+    """
+    Without extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper())
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, yaml_type))
+    qtbot.mouseClick(runner.weap_head_save, Qt.LeftButton)
+    if path.exists(temp + extn):
+        remove(temp + extn)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Weapon data has been successfully saved."
+
+    """
+    With extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper()) + extn
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, yaml_type))
+    qtbot.mouseClick(runner.weap_head_save, Qt.LeftButton)
+    if path.exists(temp):
+        remove(temp)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Weapon data has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "type, char",
+    [
+        pytest.param("Bow", "Venti", id="face.wind.rule: Saving a random weapon of bow type actually as a JSON file"),
+        pytest.param("Catalyst", "Nahida", id="face.wind.rule: Saving a random weapon of catalyst type actually as a JSON file"),
+        pytest.param("Claymore", "Navia", id="face.wind.rule: Saving a random weapon of claymore type actually as a JSON file"),
+        pytest.param("Polearm", "Raiden Shogun", id="face.wind.rule: Saving a random weapon of polearm type actually as a JSON file"),
+        pytest.param("Sword", "Furina", id="face.wind.rule: Saving a random weapon of sword type actually as a JSON file"),
+    ]
+)
+def test_weap_save_json_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, type: str, char: str) -> None:
+    """
+    Test saving a random weapon of a certain type as a JSON file
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    name = choice(list(Family[type].keys()))
+    objc = Family[type][name]()
+    levl = choice(objc.levl_bind).value.name
+
+    runner.head_char_name.setCurrentText(char)
+    runner.weap_area_name.setCurrentText(name)
+    runner.weap_area_levl.setCurrentText(levl)
+    """
+    Perform the action of saving the weapon information
+    """
+    extn = ".json"
+
+    """
+    Without extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper())
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, json_type))
+    qtbot.mouseClick(runner.weap_head_save, Qt.LeftButton)
+    if path.exists(temp + extn):
+        remove(temp + extn)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Weapon data has been successfully saved."
+
+    """
+    With extension
+    """
+    temp = str(Path(gettempdir()) / uuid4().hex[0:8].upper()) + extn
+    mocker.patch.object(QFileDialog, "getSaveFileName", return_value=(temp, json_type))
+    qtbot.mouseClick(runner.weap_head_save, Qt.LeftButton)
+    if path.exists(temp):
+        remove(temp)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Weapon data has been successfully saved."
+
+
+@pytest.mark.parametrize(
+    "char, sample",
+    [
+        pytest.param("Venti", yaml_bow_sample, id="face.wind.rule: Loading a weapon of bow type actually from YAML file"),
+        pytest.param("Nahida", yaml_catalyst_sample, id="face.wind.rule: Loading a weapon of catalyst type actually from YAML file"),
+        pytest.param("Navia", yaml_claymore_sample, id="face.wind.rule: Loading a weapon of claymore type actually from YAML file"),
+        pytest.param("Raiden Shogun", yaml_polearm_sample, id="face.wind.rule: Loading a weapon of polearm type actually from YAML file"),
+        pytest.param("Furina", yaml_sword_sample, id="face.wind.rule: Loading a weapon of sword type actually from YAML file"),
+    ]
+)
+def test_weap_load_yaml_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, sample: str) -> None:
+    """
+    Test loading a weapon of a certain type from YAML file
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of loading the weapon information
+    """
+    extn = ".yaml"
+    temp = NamedTemporaryFile(prefix="gi-loadouts-", suffix=extn, delete=False, mode="w")
+    temp.write(sample)
+    temp.close()
+
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(temp.name, yaml_type))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Weapon data has been successfully loaded."
+
+    """
+    Cleanup the temporary files
+    """
+    remove(temp.name)
+
+
+@pytest.mark.parametrize(
+    "char, sample",
+    [
+        pytest.param("Venti", json_bow_sample, id="face.wind.rule: Loading a weapon of bow type actually from JSON file"),
+        pytest.param("Nahida", json_catalyst_sample, id="face.wind.rule: Loading a weapon of catalyst type actually from JSON file"),
+        pytest.param("Navia", json_claymore_sample, id="face.wind.rule: Loading a weapon of claymore type actually from JSON file"),
+        pytest.param("Raiden Shogun", json_polearm_sample, id="face.wind.rule: Loading a weapon of polearm type actually from JSON file"),
+        pytest.param("Furina", json_sword_sample, id="face.wind.rule: Loading a weapon of sword type actually from JSON file"),
+    ]
+)
+def test_weap_load_json_actual(runner: MainWindow, qtbot: QtBot, mocker: MockerFixture, char: str, sample: str) -> None:
+    """
+    Test loading a weapon of a certain type from JSON file
+
+    :return:
+    """
+
+    """
+    Set the user interface elements as intended
+    """
+    runner.head_char_name.setCurrentText(char)
+
+    """
+    Perform the action of loading the weapon information
+    """
+    extn = ".json"
+    temp = NamedTemporaryFile(prefix="gi-loadouts-", suffix=extn, delete=False, mode="w")
+    temp.write(sample)
+    temp.close()
+
+    mocker.patch.object(QFileDialog, "getOpenFileName", return_value=(temp.name, json_type))
+    qtbot.mouseClick(runner.weap_head_load, Qt.LeftButton)
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert runner.statarea.currentMessage() == "Weapon data has been successfully loaded."
+
+    """
+    Cleanup the temporary files
+    """
+    remove(temp.name)
diff --git a/test/static/gi-loadouts-ocr-test-ccol.png b/test/static/gi-loadouts-ocr-test-ccol.png
new file mode 100644
index 00000000..4fef8dc4
Binary files /dev/null and b/test/static/gi-loadouts-ocr-test-ccol.png differ
diff --git a/test/static/gi-loadouts-ocr-test-fwol.png b/test/static/gi-loadouts-ocr-test-fwol.png
new file mode 100644
index 00000000..1f34b947
Binary files /dev/null and b/test/static/gi-loadouts-ocr-test-fwol.png differ
diff --git a/test/static/gi-loadouts-ocr-test-gboe.png b/test/static/gi-loadouts-ocr-test-gboe.png
new file mode 100644
index 00000000..6a31e5a8
Binary files /dev/null and b/test/static/gi-loadouts-ocr-test-gboe.png differ
diff --git a/test/static/gi-loadouts-ocr-test-pmod.png b/test/static/gi-loadouts-ocr-test-pmod.png
new file mode 100644
index 00000000..47dab1ab
Binary files /dev/null and b/test/static/gi-loadouts-ocr-test-pmod.png differ
diff --git a/test/static/gi-loadouts-ocr-test-sdoe.png b/test/static/gi-loadouts-ocr-test-sdoe.png
new file mode 100644
index 00000000..546b6752
Binary files /dev/null and b/test/static/gi-loadouts-ocr-test-sdoe.png differ
diff --git a/test/test_main.py b/test/test_main.py
new file mode 100644
index 00000000..1d014e5c
--- /dev/null
+++ b/test/test_main.py
@@ -0,0 +1,28 @@
+import pytest
+
+from gi_loadouts import main
+
+
+@pytest.mark.parametrize(
+    "_",
+    [
+        pytest.param(None, id="main: Attempt loading the custom fonts")
+    ]
+)
+def test_load_custom_font(runner, mocker, _):
+    """
+    Test loading of custom fonts
+
+    :return:
+    """
+
+    """
+    Mock the loading of custom font in the user interface
+    """
+    mocker.patch("gi_loadouts.main.QFontDatabase.addApplicationFont", return_value=1)
+    main.load_custom_font()
+
+    """
+    Confirm if the user interface elements change accordingly
+    """
+    assert main.QFontDatabase.addApplicationFont.call_count == 5
diff --git a/test/type/__init__.py b/test/type/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/type/test_char.py b/test/type/test_char.py
new file mode 100644
index 00000000..d10583f6
--- /dev/null
+++ b/test/type/test_char.py
@@ -0,0 +1,22 @@
+import pytest
+
+from gi_loadouts.type.char.cons import Cons, ConsType, __cons__
+
+
+@pytest.mark.parametrize(
+    "text, name, data",
+    [
+        pytest.param(
+            item.replace(" ", "_"), item, int(item.split(" ")[1]), id=f"type.char.cons: {item}"
+        ) for item in __cons__
+    ]
+)
+def test_char(text: str, name: str, data: int) -> None:
+    """
+    Test type of constellation
+
+    :return:
+    """
+    unit = ConsType(name=name, data=data)
+    csut = getattr(Cons, text)
+    assert unit == csut.value
diff --git a/test/type/test_levl.py b/test/type/test_levl.py
new file mode 100644
index 00000000..10cdbd0b
--- /dev/null
+++ b/test/type/test_levl.py
@@ -0,0 +1,31 @@
+import pytest
+
+from gi_loadouts.type.levl import Level, LevelType, __level__
+from gi_loadouts.type.rank import Rank
+
+
+@pytest.mark.parametrize(
+    "text, qant, rank, name",
+    [
+        pytest.param(
+            item.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_"),
+            int(item.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_").split("_")[1]),
+            int(item.replace(" ", "_").replace("(", "").replace(")", "").replace("/", "_").split("_")[4]),
+            item,
+            id=f"type.levl: {item}"
+        ) for item in __level__.keys()
+    ]
+)
+def test_levl(text: str, qant: int, rank: int, name: str) -> None:
+    """
+    Test type of level
+
+    :return:
+    """
+    unit = LevelType(
+        qant = qant,
+        rank = getattr(Rank, f"Rank_{rank}"),
+        name = name
+    )
+    lvut = getattr(Level, text)
+    assert unit == lvut.value
diff --git a/test/type/test_rank.py b/test/type/test_rank.py
new file mode 100644
index 00000000..a9d02636
--- /dev/null
+++ b/test/type/test_rank.py
@@ -0,0 +1,19 @@
+import pytest
+
+from gi_loadouts.type.rank import Rank, __rank__
+
+
+@pytest.mark.parametrize(
+    "text, data",
+    [
+        pytest.param(item, unit, id=f"type.rank: {item}") for item, unit in __rank__.items()
+    ]
+)
+def test_rank(text: str, data: int) -> None:
+    """
+    Test type of rank
+
+    :return:
+    """
+    rkut = getattr(Rank, text.replace(" ", "_"))
+    assert rkut.value == data
diff --git a/test/type/test_rare.py b/test/type/test_rare.py
new file mode 100644
index 00000000..62b9d42e
--- /dev/null
+++ b/test/type/test_rare.py
@@ -0,0 +1,27 @@
+import pytest
+
+from gi_loadouts.type.rare import Rare
+
+
+@pytest.mark.parametrize(
+    "indx, name, qant, back",
+    [
+        pytest.param("Star_0", "Star 0", 0, ":rare/imgs/rare/star_1.webp", id="type.rare: Star 0"),
+        pytest.param("Star_1", "Star 1", 1, ":rare/imgs/rare/star_1.webp", id="type.rare: Star 1"),
+        pytest.param("Star_2", "Star 2", 2, ":rare/imgs/rare/star_2.webp", id="type.rare: Star 2"),
+        pytest.param("Star_3", "Star 3", 3, ":rare/imgs/rare/star_3.webp", id="type.rare: Star 3"),
+        pytest.param("Star_4", "Star 4", 4, ":rare/imgs/rare/star_4.webp", id="type.rare: Star 4"),
+        pytest.param("Star_5", "Star 5", 5, ":rare/imgs/rare/star_5.webp", id="type.rare: Star 5"),
+    ]
+)
+def test_rare(indx: str, name: str, qant: int, back: str) -> None:
+    """
+    Test type of rarity
+
+    :return:
+    """
+    item = getattr(Rare, indx)
+    assert item.name == indx
+    assert item.value.name == name
+    assert item.value.qant == qant
+    assert item.value.back == back
diff --git a/test/type/test_stat.py b/test/type/test_stat.py
new file mode 100644
index 00000000..3ffa8cc6
--- /dev/null
+++ b/test/type/test_stat.py
@@ -0,0 +1,19 @@
+import pytest
+
+from gi_loadouts.type.stat import ATTR, STAT, __revmap__
+
+
+@pytest.mark.parametrize(
+    "text, name, qant",
+    [
+        pytest.param(item, data, 0.0, id=f"type.stat: {item}") for item, data in __revmap__.items()
+    ]
+)
+def test_attr(text: str, name: STAT, qant: float) -> None:
+    """
+    Test type of stats
+
+    :return:
+    """
+    prop = ATTR(stat_name=name, stat_data=qant)
+    assert prop.stat_name.value == text
diff --git a/test/type/test_vson.py b/test/type/test_vson.py
new file mode 100644
index 00000000..16dfaeb9
--- /dev/null
+++ b/test/type/test_vson.py
@@ -0,0 +1,28 @@
+import pytest
+
+from gi_loadouts.type.vson import Vision
+
+
+@pytest.mark.parametrize(
+    "indx, name, colour",
+    [
+        pytest.param("anemo", "Anemo", "rgba(126, 212, 184, 254)", id="type.vson: Anemo"),
+        pytest.param("cryo", "Cryo", "rgba(164, 226, 240, 254)", id="type.vson: Cryo"),
+        pytest.param("dendro", "Dendro", "rgba(166, 202, 56, 254)", id="type.vson: Dendro"),
+        pytest.param("electro", "Electro", "rgba(180, 139, 202, 254)", id="type.vson: Electro"),
+        pytest.param("geo", "Geo", "rgba(222, 188, 108, 254)", id="type.vson: Geo"),
+        pytest.param("hydro", "Hydro", "rgba(0, 190, 254, 254)", id="type.vson: Hydro"),
+        pytest.param("pyro", "Pyro", "rgba(240, 122, 52, 254)", id="type.vson: Pyro"),
+        pytest.param("none", "None", "rgba(128, 128, 128, 254)", id="type.vson: None"),
+    ]
+)
+def test_vson(indx: str, name: str, colour: str) -> None:
+    """
+    Test type of vision
+
+    :return:
+    """
+    item = getattr(Vision, indx)
+    assert item.name == indx
+    assert item.value.name == name
+    assert item.value.colour == colour
diff --git a/test/type/test_weap.py b/test/type/test_weap.py
new file mode 100644
index 00000000..813c4caa
--- /dev/null
+++ b/test/type/test_weap.py
@@ -0,0 +1,19 @@
+import pytest
+
+from gi_loadouts.type.weap import WeaponType
+
+
+@pytest.mark.parametrize(
+    "type",
+    [
+        pytest.param(weapon.value, id=f"type.weap: {weapon.value}") for weapon in WeaponType
+    ]
+)
+def test_weap(type: str) -> None:
+    """
+    Test type of weapon
+
+    :return:
+    """
+    weapunit = WeaponType(type)
+    assert weapunit.value == type