Files
cremote/test/accessibility_integration_test.go
Josh at WLTechBlog 2817b8a049 ada tools update
2025-10-02 11:40:26 -05:00

423 lines
12 KiB
Go

package test
import (
"encoding/json"
"fmt"
"os"
"testing"
"time"
"git.teamworkapps.com/shortcut/cremote/client"
)
const (
testServerURL = "http://localhost:8080"
daemonAddr = "localhost:9223"
)
// TestAccessibilityToolsIntegration runs comprehensive integration tests for all accessibility tools
func TestAccessibilityToolsIntegration(t *testing.T) {
// Skip if not in integration test mode
if os.Getenv("INTEGRATION_TESTS") != "true" {
t.Skip("Skipping integration tests. Set INTEGRATION_TESTS=true to run.")
}
// Create client
c := client.NewClient(daemonAddr)
// Wait for daemon to be ready
time.Sleep(2 * time.Second)
// Navigate to test page
err := c.Navigate("", testServerURL+"/test-accessible.html", false, 10)
if err != nil {
t.Fatalf("Failed to navigate to test page: %v", err)
}
// Wait for page load
time.Sleep(2 * time.Second)
// Run all accessibility tests
t.Run("AxeCoreIntegration", testAxeCoreIntegration(c))
t.Run("ContrastChecking", testContrastChecking(c))
t.Run("KeyboardNavigation", testKeyboardNavigation(c))
t.Run("ZoomTesting", testZoomTesting(c))
t.Run("ReflowTesting", testReflowTesting(c))
t.Run("ScreenshotEnhancements", testScreenshotEnhancements(c))
t.Run("LibraryInjection", testLibraryInjection(c))
t.Run("AccessibilityTree", testAccessibilityTree(c))
}
// testAxeCoreIntegration tests axe-core injection and execution
func testAxeCoreIntegration(c *client.Client) func(*testing.T) {
return func(t *testing.T) {
// Inject axe-core
err := c.InjectAxeCore("", "4.8.0", 30)
if err != nil {
t.Fatalf("Failed to inject axe-core: %v", err)
}
// Run axe tests
runOnly := []string{"wcag2a", "wcag2aa"}
result, err := c.RunAxeCore("", runOnly, nil, 30)
if err != nil {
t.Fatalf("Failed to run axe-core: %v", err)
}
// Verify result structure
if result == nil {
t.Fatal("Axe result is nil")
}
// Check that we got some results
totalChecks := len(result.Violations) + len(result.Passes) + len(result.Incomplete) + len(result.Inapplicable)
if totalChecks == 0 {
t.Error("Axe-core returned no results")
}
t.Logf("Axe-core results: %d violations, %d passes, %d incomplete, %d inapplicable",
len(result.Violations), len(result.Passes), len(result.Incomplete), len(result.Inapplicable))
// For accessible page, we expect few or no violations
if len(result.Violations) > 5 {
t.Errorf("Expected few violations on accessible page, got %d", len(result.Violations))
}
}
}
// testContrastChecking tests color contrast checking
func testContrastChecking(c *client.Client) func(*testing.T) {
return func(t *testing.T) {
result, err := c.CheckContrast("", "body", 10)
if err != nil {
t.Fatalf("Failed to check contrast: %v", err)
}
if result == nil {
t.Fatal("Contrast result is nil")
}
// Verify result structure
if result.TotalElements == 0 {
t.Error("No elements checked for contrast")
}
t.Logf("Contrast check: %d elements, %d AA pass, %d AA fail, %d AAA pass, %d AAA fail",
result.TotalElements, result.WCAGAAPass, result.WCAGAAFail, result.WCAGAAAPass, result.WCAGAAAFail)
// For accessible page, we expect high AA pass rate
if result.TotalElements > 0 {
passRate := float64(result.WCAGAAPass) / float64(result.TotalElements)
if passRate < 0.8 {
t.Errorf("Expected high AA pass rate on accessible page, got %.2f%%", passRate*100)
}
}
// Verify violation details
for _, violation := range result.Violations {
if violation.Ratio == 0 {
t.Error("Violation has zero contrast ratio")
}
if violation.Element == "" {
t.Error("Violation missing element selector")
}
}
}
}
// testKeyboardNavigation tests keyboard navigation testing
func testKeyboardNavigation(c *client.Client) func(*testing.T) {
return func(t *testing.T) {
result, err := c.TestKeyboard("", 10)
if err != nil {
t.Fatalf("Failed to test keyboard navigation: %v", err)
}
if result == nil {
t.Fatal("Keyboard test result is nil")
}
// Verify result structure
if result.TotalElements == 0 {
t.Error("No interactive elements found")
}
t.Logf("Keyboard test: %d elements, %d focusable, %d not focusable, %d missing focus indicator, %d keyboard traps",
result.TotalElements, result.Focusable, result.NotFocusable, result.MissingFocusIndicator, result.KeyboardTraps)
// For accessible page, we expect high focusability rate
if result.TotalElements > 0 {
focusableRate := float64(result.Focusable) / float64(result.TotalElements)
if focusableRate < 0.8 {
t.Errorf("Expected high focusability rate on accessible page, got %.2f%%", focusableRate*100)
}
}
// Verify tab order
if len(result.TabOrder) == 0 {
t.Error("Tab order is empty")
}
// Verify issue details
for _, issue := range result.Issues {
if issue.Type == "" {
t.Error("Issue missing type")
}
if issue.Element == "" {
t.Error("Issue missing element selector")
}
}
}
}
// testZoomTesting tests zoom functionality testing
func testZoomTesting(c *client.Client) func(*testing.T) {
return func(t *testing.T) {
zoomLevels := []float64{1.0, 2.0, 4.0}
result, err := c.TestZoom("", zoomLevels, 10)
if err != nil {
t.Fatalf("Failed to test zoom: %v", err)
}
if result == nil {
t.Fatal("Zoom test result is nil")
}
// Verify we tested all zoom levels
if len(result.ZoomLevels) != len(zoomLevels) {
t.Errorf("Expected %d zoom levels, got %d", len(zoomLevels), len(result.ZoomLevels))
}
t.Logf("Zoom test: tested %d levels, found %d issues", len(result.ZoomLevels), len(result.Issues))
// Verify zoom level details
for _, zoomTest := range result.ZoomLevels {
if zoomTest.ZoomLevel == 0 {
t.Error("Zoom level is zero")
}
if zoomTest.ViewportWidth == 0 || zoomTest.ViewportHeight == 0 {
t.Error("Viewport dimensions are zero")
}
// For accessible page at 200%, we expect no horizontal scroll
if zoomTest.ZoomLevel == 2.0 && zoomTest.HasHorizontalScroll {
t.Error("Expected no horizontal scroll at 200% zoom on accessible page")
}
}
}
}
// testReflowTesting tests responsive reflow testing
func testReflowTesting(c *client.Client) func(*testing.T) {
return func(t *testing.T) {
widths := []int{320, 1280}
result, err := c.TestReflow("", widths, 10)
if err != nil {
t.Fatalf("Failed to test reflow: %v", err)
}
if result == nil {
t.Fatal("Reflow test result is nil")
}
// Verify we tested all widths
if len(result.Breakpoints) != len(widths) {
t.Errorf("Expected %d breakpoints, got %d", len(widths), len(result.Breakpoints))
}
t.Logf("Reflow test: tested %d breakpoints, found %d issues", len(result.Breakpoints), len(result.Issues))
// Verify breakpoint details
for _, breakpoint := range result.Breakpoints {
if breakpoint.Width == 0 {
t.Error("Breakpoint width is zero")
}
if breakpoint.Height == 0 {
t.Error("Breakpoint height is zero")
}
// For accessible page at 320px, we expect no horizontal scroll
if breakpoint.Width == 320 && breakpoint.HasHorizontalScroll {
t.Error("Expected no horizontal scroll at 320px on accessible page")
}
// Verify responsive layout
if !breakpoint.ResponsiveLayout {
t.Errorf("Expected responsive layout at %dpx", breakpoint.Width)
}
}
}
}
// testScreenshotEnhancements tests enhanced screenshot functionality
func testScreenshotEnhancements(c *client.Client) func(*testing.T) {
return func(t *testing.T) {
// Test basic screenshot
err := c.TakeScreenshot("", "/tmp/test-screenshot.png", false, 5)
if err != nil {
t.Fatalf("Failed to take screenshot: %v", err)
}
// Verify file exists
if _, err := os.Stat("/tmp/test-screenshot.png"); os.IsNotExist(err) {
t.Error("Screenshot file was not created")
}
// Clean up
os.Remove("/tmp/test-screenshot.png")
t.Log("Screenshot functionality verified")
}
}
// testLibraryInjection tests library injection via console_command
func testLibraryInjection(c *client.Client) func(*testing.T) {
return func(t *testing.T) {
// Test injecting jQuery
params := map[string]string{
"command": "typeof jQuery",
"inject_library": "jquery",
"timeout": "10",
}
resp, err := c.SendCommand("console-command", params)
if err != nil {
t.Fatalf("Failed to execute console command with library injection: %v", err)
}
if !resp.Success {
t.Fatalf("Console command failed: %s", resp.Error)
}
// Verify jQuery was injected
if resp.Data == nil {
t.Error("Console command returned no data")
}
t.Log("Library injection functionality verified")
}
}
// testAccessibilityTree tests accessibility tree retrieval
func testAccessibilityTree(c *client.Client) func(*testing.T) {
return func(t *testing.T) {
depth := 3
result, err := c.GetAccessibilityTree("", &depth, 10)
if err != nil {
t.Fatalf("Failed to get accessibility tree: %v", err)
}
if result == nil {
t.Fatal("Accessibility tree result is nil")
}
// Verify we got nodes
if len(result.Nodes) == 0 {
t.Error("Accessibility tree has no nodes")
}
t.Logf("Accessibility tree: %d nodes", len(result.Nodes))
// Verify node structure
foundRootNode := false
for _, node := range result.Nodes {
if node.NodeID == "" {
t.Error("Node missing NodeID")
}
// Check for root node
if node.Role != nil && node.Role.Value == "WebArea" {
foundRootNode = true
}
}
if !foundRootNode {
t.Error("No root node found in accessibility tree")
}
}
}
// TestInaccessiblePage tests tools against a page with known accessibility issues
func TestInaccessiblePage(t *testing.T) {
// Skip if not in integration test mode
if os.Getenv("INTEGRATION_TESTS") != "true" {
t.Skip("Skipping integration tests. Set INTEGRATION_TESTS=true to run.")
}
c := client.NewClient(daemonAddr)
// Navigate to inaccessible test page
err := c.Navigate("", testServerURL+"/test-inaccessible.html", false, 10)
if err != nil {
t.Fatalf("Failed to navigate to test page: %v", err)
}
time.Sleep(2 * time.Second)
// Inject and run axe-core
err = c.InjectAxeCore("", "4.8.0", 30)
if err != nil {
t.Fatalf("Failed to inject axe-core: %v", err)
}
runOnly := []string{"wcag2a", "wcag2aa"}
result, err := c.RunAxeCore("", runOnly, nil, 30)
if err != nil {
t.Fatalf("Failed to run axe-core: %v", err)
}
// For inaccessible page, we expect violations
if len(result.Violations) == 0 {
t.Error("Expected violations on inaccessible page, got none")
}
t.Logf("Found %d violations on inaccessible page (expected)", len(result.Violations))
// Test contrast checking - expect failures
contrastResult, err := c.CheckContrast("", "body", 10)
if err != nil {
t.Fatalf("Failed to check contrast: %v", err)
}
if contrastResult.WCAGAAFail == 0 {
t.Error("Expected contrast failures on inaccessible page, got none")
}
t.Logf("Found %d contrast failures on inaccessible page (expected)", contrastResult.WCAGAAFail)
}
// BenchmarkAxeCore benchmarks axe-core execution
func BenchmarkAxeCore(b *testing.B) {
if os.Getenv("INTEGRATION_TESTS") != "true" {
b.Skip("Skipping integration tests. Set INTEGRATION_TESTS=true to run.")
}
c := client.NewClient(daemonAddr)
// Navigate once
c.Navigate("", testServerURL+"/test-accessible.html", false, 10)
time.Sleep(2 * time.Second)
// Inject once
c.InjectAxeCore("", "4.8.0", 30)
b.ResetTimer()
for i := 0; i < b.N; i++ {
runOnly := []string{"wcag2aa"}
_, err := c.RunAxeCore("", runOnly, nil, 30)
if err != nil {
b.Fatalf("Failed to run axe-core: %v", err)
}
}
}
// Helper function to pretty print JSON
func prettyPrint(v interface{}) string {
b, err := json.MarshalIndent(v, "", " ")
if err != nil {
return fmt.Sprintf("%+v", v)
}
return string(b)
}