Actions & Interactions
Specter provides native commands to simulate realistic player input, trigger game logic, and interact with UMG UI widgets. All actions automatically wait for elements to be ready, eliminating flakiness without manual sleep() calls.
UMG UI Interactions
Interacting with native UUserWidget buttons, text inputs, and sliders is identical to standard Playwright web automation. Specter translates these calls into native Slate/UMG pointer and keyboard events in Unreal Engine.
const menu = world.widget('WBP_MainMenu');
// Click a button by text
await menu.getByText('Start Game').click();
// Type into a UEditableTextBox
await menu.getByRole('textbox', { name: 'Server Name' }).fill('NA-East-01');
// Hover to trigger tooltips or animations
await menu.getByRole('button', { name: 'Settings' }).hover();Note: click() automatically waits for the widget to be visible, enabled, and not occluded by other widgets before dispatching the pointer event.
Gameplay Ability System (GAS)
If your game uses GAS, you can bypass complex input sequences and directly trigger abilities by their Gameplay Tag. This is incredibly useful for testing isolated combat mechanics.
const player = world.actor('HeroCharacter');
// Activates the ability bound to this tag
await player.triggerAbilityByTag('Ability.Skill.Fireball');
// Cancels an active ability
await player.cancelAbility('/Game/Abilities/GA_Channeling');Native Input Simulation
To test character movement or complex input combos, use world.keyboard to dispatch virtual key presses or player.triggerInputAction() to dispatch Enhanced Input actions.
// Press a specific virtual key
await world.keyboard.press('SpaceBar');
// Press and hold for charging mechanics (duration in ms)
await world.keyboard.press('W', 1000);
// Or hold a key down indefinitely while doing other things
await world.keyboard.down('W');
await player.waitForGameplayTag('Movement.Sprinting');
await world.keyboard.up('W');
// Execute complex Enhanced Input Action Mappings directly
await player.triggerInputAction('IA_Jump', true);Actor Transforms & Teleportation
Sometimes you just need to move an actor immediately to test a specific area or trigger volume without manually walking them there.
const player = world.actor('HeroCharacter');
const bossArena = world.actor('BossTriggerVolume');
// Get destination coords
const dest = await bossArena.getLocation();
// Teleport the player instantly via SetActorLocation
await player.setLocation({ x: dest.x, y: dest.y, z: dest.z + 100 });Calling UFUNCTIONs Natively
You can invoke any Blueprint or C++ function natively on an actor using the callFunction API. It automatically handles sending payload arguments, processing the event, and extracting whichever data type comes out the other end.
✨ Full Autocomplete & Type Safety
If you have generated your Strongly-Typed Blueprints (via Tools → Programming → Generate Specter TS Types in Unreal Engine), callFunction becomes fully type-safe, completely eliminating the need to guess argument signatures or blindly cast return data.
- Function Names: VS Code provides a strict autocomplete dropdown of all available Blueprint functions and Custom Events for that specific class.
- Arguments: Enforces compile-time validation of the required arguments object (e.g.
{ NewLocation: Vector3, bSweep: boolean }), mapped directly from the Blueprint pins. - Return Types: The Promise return type is strictly inferred (e.g.,
Promise<number>instead ofPromise<any>). - Inheritance: Deep inheritance tracking is fully supported. If
BP_Hero_Steelinherits fromBP_Hero_Base, autocomplete correctly includes all base functions just like in C++.
1. Getting a Value (No Arguments, Complex Return)
Because callFunction automatically serializes Unreal Engine structs and primitives, you can easily pull data like FVector directly into TypeScript.
// Calls Unreal's K2_GetActorLocation and automatically parses the FVector return
const location = await actor.callFunction("K2_GetActorLocation");
console.log(`Actor is at X: ${location.X}, Y: ${location.Y}, Z: ${location.Z}`);2. Setting a Value (With Arguments, No Return)
You can pass arguments exactly matching the Blueprint parameter names (including structs like FVector and FRotator).
// Calls K2_SetActorLocation, passing in a struct and boolean parameters
await actor.callFunction("K2_SetActorLocation", {
NewLocation: { X: 0, Y: 0, Z: 1000 },
bSweep: false,
bTeleport: true,
SweepHitResult: {} // Required by Unreal's signature
});3. The Best of Both Worlds (Custom Functions)
This is where the API shines. You can pass inputs in, and read the calculated returnValue or out parameters back out in a single call.
// Suppose you have a custom BP function: CalculateDamage(float BaseDamage, bool bIsCritical) -> float
const damageAmount = await actor.callFunction("CalculateDamage", {
BaseDamage: 50.0,
bIsCritical: true
});
console.log(`The final parsed damage is: ${damageAmount}`);4. Reading Object References
If a function returns an Unreal Engine Object or Actor reference (like an AActor*), callFunction automatically extracts the exact runtime Name of the object and returns it as a string. You can use this to dynamically chain locators!
// 1. Call the Blueprint function to get the equipped weapon.
// Under the hood, C++ sees the AActor* return type, grabs its Name, and passes it to TS.
const weaponName = await actor.callFunction("GetEquippedWeapon");
// 2. Use the returned exact name to create a new Specter ActorLocator
const weapon = world.actor(weaponName);
// 3. Now you have full automation control over the equipped weapon!
const ammo = await weapon.callFunction("GetCurrentAmmo");
await expect(ammo).toBeGreaterThan(0);5. Passing Object References (Locators)
If a Blueprint function requires an Actor or Object pointer (e.g., FObjectProperty or FClassProperty), you don't have to extract and pass internal string names. Specter natively accepts Locators (like ActorLocator or WidgetLocator) directly into the arguments! Specter serializes the locator's query pipeline and evaluates it against the live game world, piping the exact UObject* pointer directly into the Blueprint virtual machine.
import { test, expect } from '@specter-automation/test';
test('Can assign target actor via Blueprint function', async ({ world }) => {
// 1. Resolve your actors using locators
const enemy = world.actor('BP_EnemyBase');
const player = world.actor('BP_Hero_Steel');
// 2. Pass the locator natively into the strongly-typed callFunction arguments!
await enemy.callFunction('SetCurrentTarget', {
TargetActor: player
});
});Note: The "Generate Specter TS Types" tool automatically types these Blueprint properties as SpecterLocator | string, ensuring full compile-time validation.
6. Passing Static Assets (Textures, Materials, etc.)
If a Blueprint function requires an Asset pointer (e.g., UTexture2D* or UDataAsset*), you cannot use a Locator because the asset is not a live object in the world. Instead, you can pass its string path.
LoadObject) before executing the function.import { test, expect } from '@specter-automation/test';
test('Can update terrain weightmap via Blueprint function', async ({ world }) => {
const terrain = world.actor('BP_DynamicTerrain');
// Pass the exact string from Unreal's "Copy Reference" action!
// Specter will automatically strip the prefix, dynamically load the asset,
// and pass the correct UTexture* to the Blueprint VM.
await terrain.callFunction('SetWeightmap', {
Weightmap_RT: "Texture2D'/Game/Environment/Textures/RT_Weightmap.RT_Weightmap'"
});
});Note: Just like locators, the "Generate Specter TS Types" tool types these properties as SpecterLocator | string to support this pattern.
7. Type-Safe Blueprint Reflection & Overloaded callFunction
Specter's reflection engine extracts UFUNCTION metadata directly from Unreal Engine reflection tables, generating TypeScript overloads that enforce compile-time verification across all actor components and sub-objects (such as HotsHeroComponent).
import { test, expect } from '@specter-automation/test';
test('Select talent via type-safe Blueprint reflection', async ({ world, playerActor }) => {
// 1. Query the specific actor component
const heroComponent = playerActor.getComponent('HotsHeroComponent');
// 2. Type-safe callFunction with autocomplete on function names, argument objects, and return types
await heroComponent.callFunction('SelectTalent', {
TalentTier: 1,
TalentIndex: 0
});
// 3. Query return value with full compile-time type inference
const selectedTalent = await heroComponent.callFunction('GetSelectedTalent', {
TalentTier: 1
});
expect(selectedTalent).toBe('Talent_ShieldBash_Cooldown');
});Key Benefit: Typos in function names or argument payloads fail immediately at compile time via tsc before running a single frame in Unreal Engine.