Files
MosisService/designer/test/lib/utils.ahk
2026-01-16 12:43:06 +01:00

299 lines
7.1 KiB
AutoHotkey

; Mosis Designer Test Utilities
; AutoHotkey v2
#Include "..\config.ahk"
; Find the designer window and return its handle
FindDesignerWindow() {
return WinExist(WINDOW_TITLE)
}
; Wait for the designer window to appear
; Returns window handle or 0 if timeout
WaitForDesignerWindow(timeout := 0) {
if (timeout = 0)
timeout := STARTUP_TIMEOUT
startTime := A_TickCount
while (A_TickCount - startTime < timeout) {
hwnd := FindDesignerWindow()
if (hwnd) {
; Give window time to fully initialize
Sleep(500)
return hwnd
}
Sleep(100)
}
return 0
}
; Get the client area position of the designer window
; Returns {x, y, w, h} or 0 if window not found
GetClientArea(hwnd := 0) {
if (hwnd = 0)
hwnd := FindDesignerWindow()
if (!hwnd)
return 0
; Get window position
WinGetPos(&winX, &winY, &winW, &winH, hwnd)
; Get client area - for GLFW windows, typically minimal border
; Client area starts after title bar
; We'll estimate based on typical Windows decorations
; Title bar is usually ~30-40 pixels, borders ~8 pixels each side
; For a more accurate approach, we could use DllCall to GetClientRect
; But for GLFW windows, the client area often matches the requested size
; Calculate client area assuming standard decorations
borderWidth := 8
titleHeight := 31
return {
x: winX + borderWidth,
y: winY + titleHeight,
w: PHONE_WIDTH,
h: PHONE_HEIGHT
}
}
; Convert phone coordinates to screen coordinates
PhoneToScreen(phoneX, phoneY, hwnd := 0) {
client := GetClientArea(hwnd)
if (!client)
return 0
return {
x: client.x + phoneX,
y: client.y + phoneY
}
}
; Click at phone coordinates
ClickPhone(phoneX, phoneY, hwnd := 0) {
if (hwnd = 0)
hwnd := FindDesignerWindow()
if (!hwnd) {
LogMessage("ERROR: Designer window not found")
return false
}
; Activate window first
WinActivate(hwnd)
Sleep(50)
screen := PhoneToScreen(phoneX, phoneY, hwnd)
if (!screen) {
LogMessage("ERROR: Could not convert coordinates")
return false
}
; Move and click
Click(screen.x, screen.y)
Sleep(CLICK_DELAY)
LogMessage("Clicked at phone(" . phoneX . "," . phoneY . ") -> screen(" . screen.x . "," . screen.y . ")")
return true
}
; Click on a named app icon
ClickApp(appName, hwnd := 0) {
if (!APP_POSITIONS.Has(appName)) {
LogMessage("ERROR: Unknown app: " . appName)
return false
}
pos := APP_POSITIONS[appName]
LogMessage("Clicking app: " . appName)
return ClickPhone(pos.x, pos.y, hwnd)
}
; Click on a dock item
ClickDock(appName, hwnd := 0) {
if (!DOCK_POSITIONS.Has(appName)) {
LogMessage("ERROR: Unknown dock item: " . appName)
return false
}
pos := DOCK_POSITIONS[appName]
LogMessage("Clicking dock: " . appName)
return ClickPhone(pos.x, pos.y, hwnd)
}
; Send keyboard input to the designer
SendToDesigner(keys, hwnd := 0) {
if (hwnd = 0)
hwnd := FindDesignerWindow()
if (!hwnd)
return false
WinActivate(hwnd)
Sleep(50)
Send(keys)
return true
}
; Read the log file and check for a pattern
; Returns the matching line or empty string
CheckLogFor(pattern, logFile := 0) {
if (logFile = 0)
logFile := LOG_FILE
if (!FileExist(logFile))
return ""
content := FileRead(logFile)
lines := StrSplit(content, "`n")
; Search from end (most recent)
loop lines.Length {
idx := lines.Length - A_Index + 1
line := lines[idx]
if (InStr(line, pattern))
return line
}
return ""
}
; Wait for a specific pattern to appear in the log
WaitForLog(pattern, timeout := 0, logFile := 0) {
if (timeout = 0)
timeout := NAVIGATION_TIMEOUT
if (logFile = 0)
logFile := LOG_FILE
startTime := A_TickCount
while (A_TickCount - startTime < timeout) {
result := CheckLogFor(pattern, logFile)
if (result)
return result
Sleep(100)
}
return ""
}
; Wait for navigation to a specific screen
WaitForNavigation(screenName, timeout := 0) {
pattern := "Navigated to: " . screenName
return WaitForLog(pattern, timeout)
}
; Wait for back navigation
WaitForBack(timeout := 0) {
return WaitForLog("Back to:", timeout)
}
; Take a screenshot of the designer window
CaptureScreenshot(filename, hwnd := 0) {
if (hwnd = 0)
hwnd := FindDesignerWindow()
if (!hwnd)
return false
; Ensure screenshot directory exists
if (!DirExist(SCREENSHOT_DIR))
DirCreate(SCREENSHOT_DIR)
fullPath := SCREENSHOT_DIR . "\" . filename
; Use built-in screenshot capability
; Note: This captures the entire window including decorations
try {
; Activate and bring to front
WinActivate(hwnd)
Sleep(100)
; Get window position
WinGetPos(&x, &y, &w, &h, hwnd)
; Use GDI+ or Windows API for screenshot
; For simplicity, we'll use the Snipping approach
; In production, you'd use a proper GDI+ screenshot
LogMessage("Screenshot requested: " . fullPath . " (not implemented)")
return false
} catch as e {
LogMessage("Screenshot error: " . e.Message)
return false
}
}
; Log a message with timestamp
LogMessage(msg) {
timestamp := FormatTime(, "yyyy-MM-dd HH:mm:ss")
line := "[" . timestamp . "] " . msg . "`n"
; Write to console
OutputDebug(line)
; Also append to a test log file
testLog := A_ScriptDir . "\test_run.log"
FileAppend(line, testLog)
}
; Clear test logs
ClearLogs() {
testLog := A_ScriptDir . "\test_run.log"
if (FileExist(testLog))
FileDelete(testLog)
if (FileExist(LOG_FILE))
FileDelete(LOG_FILE)
}
; Close the designer window
CloseDesigner(hwnd := 0) {
if (hwnd = 0)
hwnd := FindDesignerWindow()
if (hwnd) {
WinClose(hwnd)
Sleep(500)
}
}
; Kill any running designer processes
KillDesigner() {
try {
Run('taskkill /F /IM mosis-designer.exe', , "Hide")
}
Sleep(500)
}
; Start the designer process with log redirection
; Returns the process ID or 0 on failure
StartDesigner(rmlFile := 0) {
if (rmlFile = 0)
rmlFile := HOME_RML
; Build command with output redirection
cmd := '"' . DESIGNER_EXE . '" "' . rmlFile . '"'
LogMessage("Starting designer: " . cmd)
; Start process and capture output
; We'll redirect to a log file
fullCmd := A_ComSpec . ' /c "' . cmd . '" > "' . LOG_FILE . '" 2>&1'
Run(fullCmd, A_ScriptDir, "Hide", &pid)
if (pid) {
LogMessage("Designer started with PID: " . pid)
return pid
}
LogMessage("ERROR: Failed to start designer")
return 0
}
; Assert helper - logs pass/fail and returns result
Assert(condition, testName) {
if (condition) {
LogMessage("PASS: " . testName)
return true
} else {
LogMessage("FAIL: " . testName)
return false
}
}