Skip to content

About

WoW UI Simulator - Load and render WoW addons outside the game

Resources

Stars

51 stars

Watchers

1 watching

Forks

Latest commit

 

History

20,558 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WoW UI Simulator

A World of Warcraft UI simulator for addon testing and UI rendering. Supports headless test workflows plus frame-tree and screenshot output — no WoW client required.

Community

Join the wowless Discord: https://discord.gg/rTwWcfJXuz — we have a #wow-ui-sim channel there.

UI Gallery

Main Screen Spellbook
Main screen Spellbook
Character Panel Reputations Panel
Character panel Reputations panel
Blacksmithing Guild Panel
Blacksmithing Guild panel
Store Achievements Panel
Store Achievements panel
World Map Housing Panel
World map Housing panel
Talents Panel Damage Meter
Talents panel Damage meter
Game Menu
Game menu

GitHub Action

Run your addon's test suite in CI:

- uses: osso/wow-ui-sim@12.0.5
  with:
    addon: MyAddon

The action ref matches the WoW interface version it ships against (e.g. @12.0.5). Use @master to track unreleased changes.

Tests live in Interface/AddOns/MyAddon/tests/*.lua:

test("frame name matches", function()
    local f = CreateFrame("Frame", "MyFrame")
    assertEquals("MyFrame", f:GetName())
end)

A minimal end-to-end example is at Osso/test-wow-addon — a TOC, one Lua file, a tests/ folder, and a workflow that calls this action.

Configuration

WoW install location

CASC profiles need a local WoW installation. wow-sim and wow-cli resolve the install root for the compiled client profile in this order:

  1. CLI override (library callers only; wow-sim passes none)
  2. WOW_INSTALL_PATH
  3. WOW_DATA_PATH (the Data directory; its parent is the root)
  4. The stored entry for the active product
  5. Automatic detection: the Battle.net Agent product.db on Windows, then common install locations

A candidate is accepted only when <root>/Data/data exists.

Settings file

The resolved path is stored per CASC product in install-paths.json:

Platform Location
Windows %APPDATA%\wow-ui-sim\install-paths.json
Linux $XDG_CONFIG_HOME/wow-ui-sim/install-paths.json (~/.config/... when unset)
macOS ~/Library/Application Support/wow-ui-sim/install-paths.json

The directory is the platform config directory (dirs::config_dir()). Entries are keyed by CASC product code, which the compiled client-* feature selects:

Key Client profile
wow Retail
wowxptr PTR
wowf WoW Forever
wow_classic Wrath of the Lich King, Mists of Pandaria
wow_classic_era Era, Anniversary

Each entry accepts install_path (the install root that contains Data) and data_path (the Data directory). install_path wins when both are present; a data_path-only entry uses its parent as the root. A complete example, also stored as docs/examples/install-paths.example.json:

{
  "install_paths": {
    "wow": {
      "install_path": "C:\\Program Files (x86)\\World of Warcraft"
    },
    "wowxptr": {
      "install_path": "D:\\Games\\World of Warcraft PTR"
    },
    "wowf": {
      "install_path": "D:\\Games\\World of Warcraft Forever"
    },
    "wow_classic": {
      "install_path": "D:\\Games\\World of Warcraft Classic",
      "data_path": "D:\\Games\\World of Warcraft Classic\\Data"
    },
    "wow_classic_era": {
      "data_path": "D:\\Games\\World of Warcraft Era\\Data"
    }
  }
}

The file is written when a valid path comes from WOW_INSTALL_PATH, WOW_DATA_PATH, or automatic detection; that entry replaces the stored one for the active product, so later launches need no variable. A valid stored entry is used as-is and not rewritten. An entry whose <root>/Data/data no longer exists is ignored with a stderr warning and replaced by the next valid source. A missing file means no stored entries; malformed content is reported and ignored, and startup continues. WOW_SIM_CASC=0 neither reads nor writes the file.

Environment variables

These are the variables a player normally sets:

Variable Effect
WOW_INSTALL_PATH WoW install root. Takes precedence over, and is stored over, the settings file.
WOW_DATA_PATH WoW Data directory; its parent is used as the root. Lower priority than WOW_INSTALL_PATH.
WOW_PRODUCT CASC product code used for detection. The compiled client-* feature fixes the client profile, so this cannot turn one client build into another.
WOW_SIM_NO_SOUND 1 or true skips audio initialization.
WOW_SIM_SOFTWARE_RENDER 1 or true forces the software GPU adapter for headless rendering (screenshot).
WOW_SIM_MAX_CORES Linux only: restrict the process to the first N CPUs (default 2).
WOW_SIM_STORAGE_DIR SavedVariables directory used when --session-wtf-path is absent.
WOW_SIM_ADDONS_PATH Extra third-party addon root during default discovery.
WOW_SIM_ADDONS_PATH_EXCLUSIVE 1 or true makes WOW_SIM_ADDONS_PATH the only addon root.

WOW_SIM_CASC=0 disables CASC asset loading, for example when game assets are unavailable.

Development and debugging variables
Variable Effect
WOW_SIM_NO_ADDONS 1 or true skips third-party addon loading.
WOW_SIM_NO_SAVED_VARS 1 or true skips loading WTF SavedVariables.
WOW_SIM_SAVE_FAILED_ADDONS 1 or true saves the SavedVariables of addons that raised Lua errors or named missing files while loading, as the client does. Failed SavedVariables input stays protected.
WOW_SIM_DEBUG_ELEMENTS 1 draws element borders and anchor points; see debug tools.
WOW_SIM_DEBUG_BORDERS 1 draws element borders only.
WOW_SIM_DEBUG_ANCHORS 1 draws anchor points only.
WOW_SIM_VERBOSE Enables verbose addon-loading logging.

Native development

Normal builds and runs use the shared desktop/local host selection:

python3 scripts/build-host.py --run
python3 scripts/build-host.py --build-host local --run
python3 scripts/build-host.py --check
python3 scripts/build-host.py --test --test integration FILTER -- --nocapture
scripts/start-profile.sh live

Use local while gaming on desktop. Raw Cargo stays local. See native workflow, profile flags, and runtime proof limits.

Rust formatting

Use rustfmt for Rust code. rust-toolchain.toml pins Rust 1.99.1 and its rustfmt component; rustfmt.toml selects the Rust 2024 syntax and style editions. Rustup selects the pinned toolchain automatically inside this checkout.

cargo fmt --all --check

To format only a file you changed, use rustfmt --config skip_children=true path/to/changed.rs; skip_children prevents formatting its child modules. Avoid workspace-wide formatting for focused changes.

Release packages

GitHub releases provide Retail and WoW Forever packages for Linux x86-64, Windows x86-64, and macOS Apple Silicon. Mists packages are available for Linux x86-64 and Windows x86-64 only. Windows packages include a ZIP and an installer; macOS packages use a DMG. Forever package names include forever, and its installer and macOS app use separate identities.

Forever packages include selected spell data. For a local Forever build, run wow-cli casc sync-forever-spells before starting the simulator. It uses the installed pinned Forever client when available, or downloads the pinned DB2 files from Blizzard's CDN. See the spell data contract.

Retail and PTR dungeon score colours require a generated cache. Run wow-cli casc sync-dungeon-score-rarity for the active profile before running local tests or using those APIs. Retail release packages and Docker images include the cache. See the rarity contract.

Docker

Published addon-test images and CI/release workflows remain separate from native development. The Dockerfile builds Retail by default; use docker build --build-arg WOW_UI_SIM_PROFILE=wowforever . for a Forever image with its spell data. Docker builds use .cargo/config.ci.toml for the default x86-64 CPU target; local builds keep target-cpu=native in .cargo/config.toml. Before publication, .github/scripts/verify_docker_image.py IMAGE checks --version and addon tests under QEMU's Nehalem CPU model as well as natively. It requires qemu-user-static (Ubuntu package); use --qemu PATH to select another static qemu-x86_64 executable. Emulation runs offline with a read-only fixture and does not require privileged containers or host binfmt changes.

# Run addon tests
docker run --rm \
  -v ./MyAddon:/app/Interface/AddOns/MyAddon \
  ghcr.io/osso/wow-ui-sim run-tests MyAddon

# Run with all Blizzard addons loaded
docker run --rm \
  -v ./MyAddon:/app/Interface/AddOns/MyAddon \
  ghcr.io/osso/wow-ui-sim --no-saved-vars run-tests MyAddon

# Render a screenshot with Mesa software Vulkan (no GPU required)
mkdir -p screenshots
docker run --rm -v "$(pwd)/screenshots:/out" \
  ghcr.io/osso/wow-ui-sim screenshot -o /out/screenshot.webp

For deterministic timing in addon tests, see the manual clock example.

The image does not include WoW game assets; screenshots may omit textures unless assets are available to the container.

Exclusive addon test runs

Default discovery combines WOW_SIM_ADDONS_PATH, local and bundled addon folders, and the selected WoW installation. Setting WOW_SIM_ADDONS_PATH alone does not isolate a test run.

Use repeatable CLI roots to replace those candidates:

wow-sim --addons-path /tmp/test/AddOns --addons-path /tmp/dependencies/AddOns run-tests MyAddon

For scripts that already set WOW_SIM_ADDONS_PATH, use the environment-only mode. No script change is needed:

WOW_SIM_ADDONS_PATH_EXCLUSIVE=1 WOW_SIM_ADDONS_PATH=/tmp/test/AddOns wow-sim run-tests MyAddon
# A script that sets WOW_SIM_ADDONS_PATH internally:
WOW_SIM_ADDONS_PATH_EXCLUSIVE=1 WOW_SIM_BIN=/path/to/wow-sim ./run-tests.sh

true also enables exclusive mode; 0/false leave default discovery active. CLI roots take precedence over environment selection. Each CLI value is one addon root, not an individual addon folder. Use absolute paths: the simulator changes its working directory to the executable directory before loading. Missing roots are omitted; an empty exclusive selection does not restore the default candidates.

Blizzard UI still loads from its profile cache. run-tests finds the bundled TestFramework explicitly, without loading other bundled addons. Exclusive mode also restricts test-file lookup and C_AddOns.LoadAddOn; it does not search ambient Interface/TestAddOns. It is not a filesystem or Lua sandbox.

GitHub Action and Docker

The composite action retains its existing default discovery. For an exclusive Docker run, mount the addon and its dependencies under a dedicated root, then pass that root to the image:

docker run --rm -v ./test-addons:/test-addons \
  ghcr.io/osso/wow-ui-sim --addons-path /test-addons run-tests MyAddon

The root must contain MyAddon/MyAddon.toc and all required addon dependencies. TestFramework does not need to be copied into that root. See the selection contract.

Utility binary builds

Integration tests build wow-sim only unless utility features are requested. The other five binaries require --features tools: wow-cli, panel-visual-metrics, bench_spellbook, bench_steady_state, and bench_talents. The two existing GUI-gated benches also require gui. Use cargo build --bin wow-cli --features tools or cargo build --bins --features tools for utility builds. The release aliases and scripts/build-host.py --bin wow-cli --features tools enable this feature automatically. Docker and full-suite test selection do not need it. Build before using bare wow-cli commands; do not pass --features tools to the executable itself. For direct runs, use cargo run --bin wow-cli --features tools -- <args>. See the build contract.

Historical replay: add --features tools before -- to the recorded Cargo utility commands in patch audit milestone tests, Mists world-map startup, and Windows port builds; original evidence is unchanged.

Attribute-only spell regeneration

wow-cli generate spells --augment-attributes adds or refreshes only SpellInfo.attributes_9 in the committed formatted spell table. It reads SpellMisc.csv from the normal WoW data directory, selects DifficultyID=0 like the full generator, and records the input hash and build 12.1.5.70391 in the output header. Existing spell IDs and all other bytes remain unchanged. This mode does not need Spell.csv; full wow-cli generate spells still requires all original CSV inputs and writes the same attribute field.

Writing Tests

Test files go in tests/ under your addon directory. Available assertions:

Function Description
assertEquals(expected, actual) Strict equality
assertNotEquals(expected, actual) Not equal
assertTrue(value) / assertFalse(value) Truthy/falsy
assertNil(value) / assertNotNil(value) Nil checks
assertError(fn) Function throws an error
assertType(expected, value) type(value) == expected
assertAlmostEquals(expected, actual, tolerance?) Float comparison (default 0.001)
assertContains(haystack, needle) String substring or table value
assertStartsWith(str, prefix) String prefix
assertEndsWith(str, suffix) String suffix
assertMatches(str, pattern) Lua pattern match
assertCount(expected, table) Table element count
assertTableEquals(expected, actual) Deep table equality
assertTableContains(table, subset) Table contains subset (deep)

Async tests for timers and callbacks:

async_test("timer fires callback", function(done)
    C_Timer.After(0, function()
        done(function()
            assertTrue(true)
        end)
    end)
end)

Admin API

The simulator exposes an A_Admin Lua namespace for controlling state in tests (player identity, combat, party, buffs, zone, economy, etc.). See docs/admin-api/.

License

GPL-3.0-only

About

WoW UI Simulator - Load and render WoW addons outside the game

Resources

Stars

51 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages