openthebox/tests/OpenTheBox.Web.Tests/TerminalHelper.cs

108 lines
3.7 KiB
C#
Raw Normal View History

using Microsoft.Playwright;
namespace OpenTheBox.Web.Tests;
/// <summary>
/// Helpers for interacting with the xterm.js terminal in Playwright tests.
/// </summary>
public static class TerminalHelper
{
/// <summary>
/// Reads the visible text content from the xterm.js terminal buffer.
/// </summary>
public static async Task<string> ReadTerminalAsync(IPage page)
{
return await page.EvaluateAsync<string>(@"() => {
const term = window.terminalInterop?.term;
if (!term) return '';
const buffer = term.buffer.active;
let text = '';
for (let i = 0; i < buffer.length; i++) {
const line = buffer.getLine(i);
if (line) text += line.translateToString(true) + '\n';
}
return text;
}");
}
/// <summary>
/// Waits until the terminal contains the specified text, with timeout.
/// </summary>
public static async Task WaitForTerminalTextAsync(IPage page, string text, int timeoutMs = 30000)
{
var deadline = DateTime.UtcNow.AddMilliseconds(timeoutMs);
while (DateTime.UtcNow < deadline)
{
var content = await ReadTerminalAsync(page);
if (content.Contains(text, StringComparison.OrdinalIgnoreCase))
return;
await Task.Delay(500);
}
// Read one final time for the error message
var finalContent = await ReadTerminalAsync(page);
throw new TimeoutException(
$"Terminal did not contain \"{text}\" within {timeoutMs}ms.\n" +
$"Terminal content:\n{finalContent}");
}
/// <summary>
/// Types text into the terminal (sends keystrokes).
/// </summary>
public static async Task TypeAsync(IPage page, string text)
{
await page.Keyboard.TypeAsync(text, new KeyboardTypeOptions { Delay = 50 });
}
/// <summary>
/// Presses Enter in the terminal.
/// </summary>
public static async Task PressEnterAsync(IPage page)
{
await page.Keyboard.PressAsync("Enter");
}
/// <summary>
/// Presses a specific key (Arrow keys, Escape, etc.).
/// </summary>
public static async Task PressKeyAsync(IPage page, string key)
{
await page.Keyboard.PressAsync(key);
}
/// <summary>
/// Sends a digit key press (1-9) for numbered menu selection.
/// </summary>
public static async Task PressDigitAsync(IPage page, int digit)
{
await page.Keyboard.PressAsync($"Digit{digit}");
}
/// <summary>
/// Waits for the terminal to be initialized (xterm.js loaded and terminal visible).
/// </summary>
public static async Task WaitForTerminalReadyAsync(IPage page, int timeoutMs = 60000)
{
var deadline = DateTime.UtcNow.AddMilliseconds(timeoutMs);
while (DateTime.UtcNow < deadline)
{
var ready = await page.EvaluateAsync<bool>(@"() => {
return window.terminalInterop?.term != null;
}");
if (ready)
{
// Verify terminal has proper dimensions (should be 120x30 after init)
var cols = await page.EvaluateAsync<int>("() => window.terminalInterop.term.cols");
if (cols < 120)
{
// Force resize if fit addon miscalculated (e.g., headless browser)
await page.EvaluateAsync("() => window.terminalInterop.term.resize(120, 30)");
await Task.Delay(200);
}
return;
}
await Task.Delay(500);
}
throw new TimeoutException($"Terminal was not initialized within {timeoutMs}ms");
}
}