423 lines
12 KiB
Go
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)
|
|
}
|
|
|