Skip to content

GUI Automation

Marc Jones edited this page Dec 30, 2025 · 1 revision

GUI Automation with Roslyn C# Scripting

PdfEditor supports Roslyn C# scripting for GUI automation and testing. Scripts can interact with the MainWindowViewModel to automate workflows.

Overview

The ScriptingService executes C# scripts with the MainWindowViewModel as the global context. This enables:

  • Automated testing of GUI workflows
  • Batch processing via the GUI
  • Scripted redaction operations
  • Integration testing

Architecture

ScriptingService

Located at PdfEditor/Services/ScriptingService.cs:

public class ScriptingService
{
    public async Task<ScriptExecutionResult> ExecuteAsync(string scriptCode);
    public async Task<ScriptExecutionResult> ExecuteFileAsync(string scriptFilePath);
    public List<string> ValidateScript(string scriptCode);
}

Script Context

Scripts run with:

  • Global this: MainWindowViewModel instance
  • Available namespaces: System, System.IO, System.Linq, PdfEditor.*
  • Type safety: Compile-time error checking
  • Async support: Full async/await

Scripting API

Available Commands

Method Description
LoadDocumentCommand(path) Load a PDF document
RedactTextCommand(text) Redact all occurrences of text
ApplyRedactionsCommand() Apply pending redactions
SaveDocumentCommand(path) Save the document
ExtractAllText() Extract all text from document

Available Properties

Property Description
CurrentDocument Info about loaded document
FilePath Current file path
PendingRedactions Collection of pending redactions
LoadDocumentTimeoutSeconds Timeout for loading (default: 30)

Example Scripts

Basic Redaction

// automation-scripts/basic-redact.csx
await LoadDocumentCommand("/path/to/document.pdf");
await RedactTextCommand("CONFIDENTIAL");
await ApplyRedactionsCommand();
await SaveDocumentCommand("/path/to/output.pdf");

Birth Certificate Test

// automation-scripts/test-birth-certificate.csx
await LoadDocumentCommand("/path/to/birth-certificate.pdf");

// Redact multiple terms
await RedactTextCommand("TORRINGTON");
await RedactTextCommand("CERTIFICATE");
await RedactTextCommand("FEE");

// Apply and save
await ApplyRedactionsCommand();
await SaveDocumentCommand("/tmp/redacted.pdf");

// Verify
var text = ExtractAllText();
if (text.Contains("TORRINGTON"))
    throw new Exception("Redaction failed: TORRINGTON still present");

return "Success";

Batch Processing

// automation-scripts/batch-redact.csx
var files = Directory.GetFiles("/data/pdfs", "*.pdf");
foreach (var file in files)
{
    await LoadDocumentCommand(file);
    await RedactTextCommand("CONFIDENTIAL");
    await ApplyRedactionsCommand();

    var output = file.Replace(".pdf", "-redacted.pdf");
    await SaveDocumentCommand(output);

    Console.WriteLine($"Processed: {file}");
}
return $"Processed {files.Length} files";

Running Scripts

From xUnit Tests

[Fact]
public async Task TestRedactionWorkflow()
{
    var viewModel = CreateTestViewModel();
    var scripting = new ScriptingService(viewModel);

    var result = await scripting.ExecuteAsync(@"
        await LoadDocumentCommand(""test.pdf"");
        await RedactTextCommand(""SECRET"");
        await ApplyRedactionsCommand();
        return PendingRedactions.Count;
    ");

    result.Success.Should().BeTrue();
    result.ReturnValue.Should().Be(0);
}

Timeout Configuration

For potentially slow operations (issue #93):

// Set longer timeout for large documents
LoadDocumentTimeoutSeconds = 60;
await LoadDocumentCommand("/path/to/large.pdf");

Script Validation

Validate scripts before execution:

var errors = scriptingService.ValidateScript(code);
if (errors.Any())
{
    Console.WriteLine("Compilation errors:");
    foreach (var error in errors)
        Console.WriteLine($"  {error}");
}

Error Handling

Scripts throw exceptions on failure:

try
{
    await LoadDocumentCommand("nonexistent.pdf");
}
catch (FileNotFoundException)
{
    Console.WriteLine("File not found");
}
catch (TimeoutException)
{
    Console.WriteLine("Load timed out");
}

Security Considerations

Current implementation:

  • Scripts have full access to MainWindowViewModel
  • No sandboxing - scripts can execute arbitrary code
  • Only run trusted scripts

Best practices:

  • Only run scripts from trusted sources
  • Review scripts before execution
  • Use in testing/development environments

Test Coverage

Script execution is tested in:

  • AutomationScriptTests.cs - Script execution tests
  • ScriptedGuiTests.cs - GUI workflow tests

Directory Structure

automation-scripts/
├── test-birth-certificate.csx
├── test-verapdf-corpus-sample.csx
├── test-birth-certificate-specific-words.csx
└── README.md

Dependencies

  • Microsoft.CodeAnalysis.CSharp.Scripting v5.0.0
  • Size impact: ~2MB (Roslyn assemblies)

Last updated: 2024-12-29

Clone this wiki locally