bump
This commit is contained in:
536
client/client.go
536
client/client.go
@@ -3497,6 +3497,542 @@ func (c *Client) CheckContrast(tabID, selector string, timeout int) (*ContrastCh
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GradientContrastResult represents the result of gradient contrast checking
|
||||
type GradientContrastResult struct {
|
||||
Selector string `json:"selector"`
|
||||
TextColor string `json:"text_color"`
|
||||
DarkestBgColor string `json:"darkest_bg_color"`
|
||||
LightestBgColor string `json:"lightest_bg_color"`
|
||||
WorstContrast float64 `json:"worst_contrast"`
|
||||
BestContrast float64 `json:"best_contrast"`
|
||||
PassesAA bool `json:"passes_aa"`
|
||||
PassesAAA bool `json:"passes_aaa"`
|
||||
RequiredAA float64 `json:"required_aa"`
|
||||
RequiredAAA float64 `json:"required_aaa"`
|
||||
IsLargeText bool `json:"is_large_text"`
|
||||
SamplePoints int `json:"sample_points"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// CheckGradientContrast checks color contrast for text on gradient backgrounds using ImageMagick
|
||||
// If tabID is empty, the current tab will be used
|
||||
// selector is required CSS selector for element with gradient background
|
||||
// timeout is in seconds, 0 means no timeout
|
||||
func (c *Client) CheckGradientContrast(tabID, selector string, timeout int) (*GradientContrastResult, error) {
|
||||
if selector == "" {
|
||||
return nil, fmt.Errorf("selector parameter is required for gradient contrast check")
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"selector": selector,
|
||||
}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeout if specified
|
||||
if timeout > 0 {
|
||||
params["timeout"] = strconv.Itoa(timeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("check-gradient-contrast", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("failed to check gradient contrast: %s", resp.Error)
|
||||
}
|
||||
|
||||
// Parse the response data
|
||||
var result GradientContrastResult
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal response data: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(dataBytes, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal gradient contrast results: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// MediaValidationResult represents the result of time-based media validation
|
||||
type MediaValidationResult struct {
|
||||
Videos []MediaElement `json:"videos"`
|
||||
Audios []MediaElement `json:"audios"`
|
||||
EmbeddedPlayers []MediaElement `json:"embedded_players"`
|
||||
TranscriptLinks []string `json:"transcript_links"`
|
||||
TotalViolations int `json:"total_violations"`
|
||||
CriticalViolations int `json:"critical_violations"`
|
||||
Warnings int `json:"warnings"`
|
||||
}
|
||||
|
||||
// MediaElement represents a video or audio element
|
||||
type MediaElement struct {
|
||||
Type string `json:"type"` // "video", "audio", "youtube", "vimeo"
|
||||
Src string `json:"src"`
|
||||
HasCaptions bool `json:"has_captions"`
|
||||
HasDescriptions bool `json:"has_descriptions"`
|
||||
HasControls bool `json:"has_controls"`
|
||||
Autoplay bool `json:"autoplay"`
|
||||
CaptionTracks []Track `json:"caption_tracks"`
|
||||
DescriptionTracks []Track `json:"description_tracks"`
|
||||
Violations []string `json:"violations"`
|
||||
Warnings []string `json:"warnings"`
|
||||
}
|
||||
|
||||
// Track represents a text track (captions, descriptions, etc.)
|
||||
type Track struct {
|
||||
Kind string `json:"kind"`
|
||||
Src string `json:"src"`
|
||||
Srclang string `json:"srclang"`
|
||||
Label string `json:"label"`
|
||||
Accessible bool `json:"accessible"`
|
||||
}
|
||||
|
||||
// ValidateMedia checks for video/audio captions, descriptions, and transcripts
|
||||
// If tabID is empty, the current tab will be used
|
||||
// timeout is in seconds, 0 means no timeout
|
||||
func (c *Client) ValidateMedia(tabID string, timeout int) (*MediaValidationResult, error) {
|
||||
params := map[string]string{}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeout if specified
|
||||
if timeout > 0 {
|
||||
params["timeout"] = strconv.Itoa(timeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("validate-media", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("failed to validate media: %s", resp.Error)
|
||||
}
|
||||
|
||||
// Parse the response data
|
||||
var result MediaValidationResult
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal response data: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(dataBytes, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal media validation results: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// HoverFocusTestResult represents the result of hover/focus content testing
|
||||
type HoverFocusTestResult struct {
|
||||
TotalElements int `json:"total_elements"`
|
||||
ElementsWithIssues int `json:"elements_with_issues"`
|
||||
PassedElements int `json:"passed_elements"`
|
||||
Issues []HoverFocusIssue `json:"issues"`
|
||||
TestedElements []HoverFocusElement `json:"tested_elements"`
|
||||
}
|
||||
|
||||
// HoverFocusElement represents an element that shows content on hover/focus
|
||||
type HoverFocusElement struct {
|
||||
Selector string `json:"selector"`
|
||||
Type string `json:"type"` // "tooltip", "dropdown", "popover", "custom"
|
||||
Dismissible bool `json:"dismissible"`
|
||||
Hoverable bool `json:"hoverable"`
|
||||
Persistent bool `json:"persistent"`
|
||||
PassesWCAG bool `json:"passes_wcag"`
|
||||
Violations []string `json:"violations"`
|
||||
}
|
||||
|
||||
// HoverFocusIssue represents a specific issue with hover/focus content
|
||||
type HoverFocusIssue struct {
|
||||
Selector string `json:"selector"`
|
||||
Type string `json:"type"` // "not_dismissible", "not_hoverable", "not_persistent"
|
||||
Severity string `json:"severity"` // "critical", "serious", "moderate"
|
||||
Description string `json:"description"`
|
||||
WCAG string `json:"wcag"` // "1.4.13"
|
||||
}
|
||||
|
||||
// TestHoverFocusContent tests WCAG 1.4.13 compliance for content on hover or focus
|
||||
// If tabID is empty, the current tab will be used
|
||||
// timeout is in seconds, 0 means no timeout
|
||||
func (c *Client) TestHoverFocusContent(tabID string, timeout int) (*HoverFocusTestResult, error) {
|
||||
params := map[string]string{}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeout if specified
|
||||
if timeout > 0 {
|
||||
params["timeout"] = strconv.Itoa(timeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("test-hover-focus", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("failed to test hover/focus content: %s", resp.Error)
|
||||
}
|
||||
|
||||
// Parse the response data
|
||||
var result HoverFocusTestResult
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal response data: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(dataBytes, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal hover/focus test results: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// TextInImagesResult represents the result of text-in-images detection
|
||||
type TextInImagesResult struct {
|
||||
TotalImages int `json:"total_images"`
|
||||
ImagesWithText int `json:"images_with_text"`
|
||||
ImagesWithoutText int `json:"images_without_text"`
|
||||
Violations int `json:"violations"`
|
||||
Warnings int `json:"warnings"`
|
||||
Images []ImageTextAnalysis `json:"images"`
|
||||
}
|
||||
|
||||
// ImageTextAnalysis represents OCR analysis of a single image
|
||||
type ImageTextAnalysis struct {
|
||||
Src string `json:"src"`
|
||||
Alt string `json:"alt"`
|
||||
HasAlt bool `json:"has_alt"`
|
||||
DetectedText string `json:"detected_text"`
|
||||
TextLength int `json:"text_length"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
IsViolation bool `json:"is_violation"`
|
||||
ViolationType string `json:"violation_type"` // "missing_alt", "insufficient_alt", "decorative_with_text"
|
||||
Recommendation string `json:"recommendation"`
|
||||
}
|
||||
|
||||
// DetectTextInImages uses Tesseract OCR to detect text in images
|
||||
// If tabID is empty, the current tab will be used
|
||||
// timeout is in seconds, 0 means no timeout
|
||||
func (c *Client) DetectTextInImages(tabID string, timeout int) (*TextInImagesResult, error) {
|
||||
params := map[string]string{}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeout if specified
|
||||
if timeout > 0 {
|
||||
params["timeout"] = strconv.Itoa(timeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("detect-text-in-images", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("failed to detect text in images: %s", resp.Error)
|
||||
}
|
||||
|
||||
// Parse the response data
|
||||
var result TextInImagesResult
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal response data: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(dataBytes, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal text-in-images results: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// CrossPageConsistencyResult represents the result of cross-page consistency checking
|
||||
type CrossPageConsistencyResult struct {
|
||||
PagesAnalyzed int `json:"pages_analyzed"`
|
||||
ConsistencyIssues int `json:"consistency_issues"`
|
||||
NavigationIssues int `json:"navigation_issues"`
|
||||
StructureIssues int `json:"structure_issues"`
|
||||
Pages []PageConsistencyAnalysis `json:"pages"`
|
||||
CommonNavigation []string `json:"common_navigation"`
|
||||
InconsistentPages []string `json:"inconsistent_pages"`
|
||||
}
|
||||
|
||||
// PageConsistencyAnalysis represents consistency analysis of a single page
|
||||
type PageConsistencyAnalysis struct {
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
HasHeader bool `json:"has_header"`
|
||||
HasFooter bool `json:"has_footer"`
|
||||
HasNavigation bool `json:"has_navigation"`
|
||||
NavigationLinks []string `json:"navigation_links"`
|
||||
MainLandmarks int `json:"main_landmarks"`
|
||||
HeaderLandmarks int `json:"header_landmarks"`
|
||||
FooterLandmarks int `json:"footer_landmarks"`
|
||||
NavigationLandmarks int `json:"navigation_landmarks"`
|
||||
Issues []string `json:"issues"`
|
||||
}
|
||||
|
||||
// CheckCrossPageConsistency analyzes multiple pages for consistency
|
||||
// If tabID is empty, the current tab will be used
|
||||
// timeout is in seconds per page, 0 means no timeout
|
||||
func (c *Client) CheckCrossPageConsistency(tabID string, urls []string, timeout int) (*CrossPageConsistencyResult, error) {
|
||||
if len(urls) == 0 {
|
||||
return nil, fmt.Errorf("no URLs provided for consistency check")
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"urls": strings.Join(urls, ","),
|
||||
}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeout if specified
|
||||
if timeout > 0 {
|
||||
params["timeout"] = strconv.Itoa(timeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("check-cross-page-consistency", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("failed to check cross-page consistency: %s", resp.Error)
|
||||
}
|
||||
|
||||
// Parse the response data
|
||||
var result CrossPageConsistencyResult
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal response data: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(dataBytes, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal cross-page consistency results: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// SensoryCharacteristicsResult represents the result of sensory characteristics detection
|
||||
type SensoryCharacteristicsResult struct {
|
||||
TotalElements int `json:"total_elements"`
|
||||
ElementsWithIssues int `json:"elements_with_issues"`
|
||||
Violations int `json:"violations"`
|
||||
Warnings int `json:"warnings"`
|
||||
Elements []SensoryCharacteristicsElement `json:"elements"`
|
||||
PatternMatches map[string]int `json:"pattern_matches"`
|
||||
}
|
||||
|
||||
// SensoryCharacteristicsElement represents an element with potential sensory-only instructions
|
||||
type SensoryCharacteristicsElement struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Text string `json:"text"`
|
||||
MatchedPatterns []string `json:"matched_patterns"`
|
||||
Severity string `json:"severity"` // "violation", "warning"
|
||||
Recommendation string `json:"recommendation"`
|
||||
}
|
||||
|
||||
// DetectSensoryCharacteristics detects instructions that rely only on sensory characteristics
|
||||
// If tabID is empty, the current tab will be used
|
||||
// timeout is in seconds, 0 means no timeout
|
||||
func (c *Client) DetectSensoryCharacteristics(tabID string, timeout int) (*SensoryCharacteristicsResult, error) {
|
||||
params := map[string]string{}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeout if specified
|
||||
if timeout > 0 {
|
||||
params["timeout"] = strconv.Itoa(timeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("detect-sensory-characteristics", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("failed to detect sensory characteristics: %s", resp.Error)
|
||||
}
|
||||
|
||||
// Parse the response data
|
||||
var result SensoryCharacteristicsResult
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal response data: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(dataBytes, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal sensory characteristics results: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// AnimationFlashResult represents the result of animation/flash detection
|
||||
type AnimationFlashResult struct {
|
||||
TotalAnimations int `json:"total_animations"`
|
||||
FlashingContent int `json:"flashing_content"`
|
||||
RapidAnimations int `json:"rapid_animations"`
|
||||
AutoplayAnimations int `json:"autoplay_animations"`
|
||||
Violations int `json:"violations"`
|
||||
Warnings int `json:"warnings"`
|
||||
Elements []AnimationFlashElement `json:"elements"`
|
||||
}
|
||||
|
||||
// AnimationFlashElement represents an animated or flashing element
|
||||
type AnimationFlashElement struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Selector string `json:"selector"`
|
||||
AnimationType string `json:"animation_type"` // "css", "gif", "video", "canvas", "svg"
|
||||
FlashRate float64 `json:"flash_rate"` // Flashes per second
|
||||
Duration float64 `json:"duration"` // Animation duration in seconds
|
||||
IsAutoplay bool `json:"is_autoplay"`
|
||||
HasControls bool `json:"has_controls"`
|
||||
CanPause bool `json:"can_pause"`
|
||||
IsViolation bool `json:"is_violation"`
|
||||
ViolationType string `json:"violation_type"`
|
||||
Recommendation string `json:"recommendation"`
|
||||
}
|
||||
|
||||
// DetectAnimationFlash detects animations and flashing content
|
||||
// If tabID is empty, the current tab will be used
|
||||
// timeout is in seconds, 0 means no timeout
|
||||
func (c *Client) DetectAnimationFlash(tabID string, timeout int) (*AnimationFlashResult, error) {
|
||||
params := map[string]string{}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeout if specified
|
||||
if timeout > 0 {
|
||||
params["timeout"] = strconv.Itoa(timeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("detect-animation-flash", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("failed to detect animation/flash: %s", resp.Error)
|
||||
}
|
||||
|
||||
// Parse the response data
|
||||
var result AnimationFlashResult
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal response data: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(dataBytes, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal animation/flash results: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// EnhancedAccessibilityResult represents enhanced accessibility tree analysis
|
||||
type EnhancedAccessibilityResult struct {
|
||||
TotalElements int `json:"total_elements"`
|
||||
ElementsWithIssues int `json:"elements_with_issues"`
|
||||
ARIAViolations int `json:"aria_violations"`
|
||||
RoleViolations int `json:"role_violations"`
|
||||
RelationshipIssues int `json:"relationship_issues"`
|
||||
LandmarkIssues int `json:"landmark_issues"`
|
||||
Elements []EnhancedAccessibilityElement `json:"elements"`
|
||||
}
|
||||
|
||||
// EnhancedAccessibilityElement represents an element with accessibility analysis
|
||||
type EnhancedAccessibilityElement struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Selector string `json:"selector"`
|
||||
Role string `json:"role"`
|
||||
AriaLabel string `json:"aria_label"`
|
||||
AriaDescribedBy string `json:"aria_described_by"`
|
||||
AriaLabelledBy string `json:"aria_labelled_by"`
|
||||
AriaRequired bool `json:"aria_required"`
|
||||
AriaInvalid bool `json:"aria_invalid"`
|
||||
AriaHidden bool `json:"aria_hidden"`
|
||||
TabIndex int `json:"tab_index"`
|
||||
IsInteractive bool `json:"is_interactive"`
|
||||
HasAccessibleName bool `json:"has_accessible_name"`
|
||||
Issues []string `json:"issues"`
|
||||
Recommendations []string `json:"recommendations"`
|
||||
}
|
||||
|
||||
// AnalyzeEnhancedAccessibility performs enhanced accessibility tree analysis
|
||||
// If tabID is empty, the current tab will be used
|
||||
// timeout is in seconds, 0 means no timeout
|
||||
func (c *Client) AnalyzeEnhancedAccessibility(tabID string, timeout int) (*EnhancedAccessibilityResult, error) {
|
||||
params := map[string]string{}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeout if specified
|
||||
if timeout > 0 {
|
||||
params["timeout"] = strconv.Itoa(timeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("analyze-enhanced-accessibility", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("failed to analyze enhanced accessibility: %s", resp.Error)
|
||||
}
|
||||
|
||||
// Parse the response data
|
||||
var result EnhancedAccessibilityResult
|
||||
dataBytes, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal response data: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(dataBytes, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal enhanced accessibility results: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// KeyboardTestResult represents the result of keyboard navigation testing
|
||||
type KeyboardTestResult struct {
|
||||
TotalInteractive int `json:"total_interactive"`
|
||||
|
||||
Reference in New Issue
Block a user