Table of Contents
- Angular Testing
- Creating an AngularHelper
- Waiting for Angular
- Router Navigation
- Component Interaction
- Change Detection
- Services
- Waiting for Components
- Events
- NgModel (Two-Way Binding)
- Practical Examples
- Testing a Login Flow
- Inspecting Component State
- Waiting for Async Data
- Router Navigation Without Page Reload
- Compatibility
- Next Steps
Angular Testing
This page covers the Angular-specific testing helpers for automating Angular applications. The AngularHelper understands Zone.js, the Angular Router, dependency injection, and change detection.
See also: Home | Getting-Started | DOM-Queries | Actions | Console-Monitoring
Creating an AngularHelper
wv, _ := webview.New(webview.WithDebugURL("http://localhost:9222"))
defer wv.Close()
ah := webview.NewAngularHelper(wv)
ah.SetTimeout(15 * time.Second) // Optional; default is 30s
The helper wraps the Webview instance and provides Angular-aware methods that account for Zone.js asynchronous processing and change detection cycles.
Waiting for Angular
WaitForAngular
The most important method. Waits for all pending Zone.js microtasks and macrotasks to complete, ensuring the application is in a stable state before you interact with it.
wv.Navigate("http://localhost:4200")
if err := ah.WaitForAngular(); err != nil {
log.Fatal("Angular did not stabilise:", err)
}
Under the hood, WaitForAngular:
- Checks whether the page is an Angular application (via
getAllAngularRootElements,[ng-version]attribute, orwindow.ng.probe) - Locates the NgZone via the root element's injector
- Subscribes to
zone.onStableand waits for it to fire - Falls back to polling
Zone.current._innerpending task flags if the injector is not accessible
This method detects both Angular 2+ and AngularJS (1.x) applications.
Angular Detection
The helper automatically detects Angular presence by checking for:
window.getAllAngularRootElements()returning elements- DOM elements with the
[ng-version]attribute window.ng.probefunction (Angular debug tools)window.angular.element(AngularJS 1.x)
If the page is not an Angular application, WaitForAngular returns an error.
Router Navigation
NavigateByRouter
Navigates using Angular's Router service directly, bypassing full page reloads. This is faster and preserves application state.
err := ah.NavigateByRouter("/dashboard/settings")
The method:
- Finds the Angular root elements
- Gets the
Routerservice from the injector - Calls
router.navigateByUrl(path) - Waits for Zone.js stability after navigation
GetRouterState
Retrieves the current Angular router state, including URL, route parameters, query parameters, and fragment.
state, err := ah.GetRouterState()
if err != nil {
log.Fatal(err)
}
log.Printf("URL: %s", state.URL)
log.Printf("Params: %v", state.Params)
log.Printf("Query: %v", state.QueryParams)
log.Printf("Fragment: %s", state.Fragment)
The AngularRouterState struct:
type AngularRouterState struct {
URL string // Current router URL
Fragment string // URL fragment (#section)
Params map[string]string // Route parameters
QueryParams map[string]string // Query string parameters
}
Component Interaction
GetComponentProperty
Reads a property value from an Angular component instance attached to a DOM element.
// Get the current value of a component property
count, err := ah.GetComponentProperty("app-counter", "count")
if err != nil {
log.Fatal(err)
}
log.Printf("Counter value: %v", count)
SetComponentProperty
Sets a property on a component and triggers change detection so the view updates.
err := ah.SetComponentProperty("app-counter", "count", 42)
The method automatically calls ApplicationRef.tick() after setting the property to ensure the view reflects the change.
CallComponentMethod
Calls a method on a component instance with optional arguments.
// Call a method with no arguments
result, err := ah.CallComponentMethod("app-cart", "clearItems")
// Call a method with arguments
result, err = ah.CallComponentMethod("app-cart", "addItem", "SKU-001", 3)
Like SetComponentProperty, this triggers change detection after the call.
Change Detection
TriggerChangeDetection
Manually triggers Angular's change detection cycle across all root components.
err := ah.TriggerChangeDetection()
This is useful after modifying the DOM or application state via JavaScript evaluation, when Angular would not automatically detect the changes.
Services
GetService
Retrieves an Angular service by its injection token name. The service is serialised to JSON and returned as a Go value.
config, err := ah.GetService("AppConfig")
if err != nil {
log.Fatal(err)
}
log.Printf("Config: %v", config)
Note that only JSON-serialisable service properties are returned. Functions, circular references, and other non-serialisable values are omitted.
Waiting for Components
WaitForComponent
Polls until an Angular component is mounted on an element matching the selector.
err := ah.WaitForComponent("app-user-profile")
This differs from WaitForSelector in that it not only waits for the DOM element to appear, but also verifies that an Angular component instance is attached to it via window.ng.probe.
Events
DispatchEvent
Dispatches a custom DOM event on an element, which Angular event bindings will pick up.
// Simple event
err := ah.DispatchEvent("app-button", "click", nil)
// Event with detail data
err = ah.DispatchEvent("app-list", "itemSelected", map[string]any{
"id": 42,
"name": "Widget",
})
The event is created with bubbles: true so it propagates through the Angular component tree.
NgModel (Two-Way Binding)
GetNgModel
Reads the value of an ngModel-bound input element.
value, err := ah.GetNgModel("input[name=email]")
log.Printf("Email: %v", value)
SetNgModel
Sets the value of an ngModel-bound input and triggers the appropriate input and change events, plus Angular change detection.
err := ah.SetNgModel("input[name=email]", "user@example.com")
This properly dispatches events so that Angular's form control system registers the change, validators run, and the model updates.
Practical Examples
Testing a Login Flow
func TestAngularLogin(t *testing.T) {
wv, _ := webview.New(webview.WithDebugURL("http://localhost:9222"))
defer wv.Close()
ah := webview.NewAngularHelper(wv)
wv.Navigate("http://localhost:4200/login")
ah.WaitForAngular()
// Fill form using ngModel
ah.SetNgModel("input[formControlName=email]", "admin@example.com")
ah.SetNgModel("input[formControlName=password]", "secret123")
// Submit
wv.Click("button[type=submit]")
ah.WaitForAngular()
// Verify navigation
state, _ := ah.GetRouterState()
if state.URL != "/dashboard" {
t.Errorf("expected /dashboard, got %s", state.URL)
}
}
Inspecting Component State
wv.Navigate("http://localhost:4200/products")
ah.WaitForAngular()
// Check how many products are loaded
count, _ := ah.GetComponentProperty("app-product-list", "products.length")
log.Printf("Products loaded: %v", count)
// Call a component method to apply a filter
ah.CallComponentMethod("app-product-list", "filterByCategory", "electronics")
ah.WaitForAngular()
// Verify the filter was applied
filteredCount, _ := ah.GetComponentProperty("app-product-list", "filteredProducts.length")
log.Printf("Filtered products: %v", filteredCount)
Waiting for Async Data
wv.Navigate("http://localhost:4200/reports")
// Wait for the component to mount
ah.WaitForComponent("app-report-viewer")
// Wait for Angular to finish all HTTP requests
ah.WaitForAngular()
// Now the data should be loaded -- capture a screenshot
data, _ := wv.Screenshot()
os.WriteFile("report.png", data, 0644)
Router Navigation Without Page Reload
wv.Navigate("http://localhost:4200")
ah.WaitForAngular()
// Navigate using Angular Router (no full page reload)
ah.NavigateByRouter("/settings/profile")
ah.WaitForAngular()
// Navigate to another route
ah.NavigateByRouter("/settings/security")
ah.WaitForAngular()
state, _ := ah.GetRouterState()
log.Printf("Current route: %s", state.URL)
Compatibility
The AngularHelper supports:
- Angular 2+ (including Angular 14, 15, 16, 17, 18, 19) via
window.ng.probeandgetAllAngularRootElements - AngularJS 1.x via
window.angular.element(detection only; Zone.js methods are specific to Angular 2+)
For the helper to work, the application must be built with Angular debug tools enabled (the default in development mode). Production builds that call enableProdMode() may disable ng.probe, in which case the helper falls back to Zone.js polling for stability checks.
Next Steps
- Actions -- Combine Angular helpers with the action sequence builder
- Console-Monitoring -- Monitor for Angular-specific errors
- DOM-Queries -- Query Angular component DOM elements
- Getting-Started -- Review connection setup