mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-20 02:15:12 +00:00
Add accessibility report cookbook recipe
Add a new cookbook recipe that generates WCAG accessibility reports using the Playwright MCP server. Includes streaming output, structured report formatting, and optional Playwright test generation. - C#, TypeScript, Python, and Go implementations - Markdown documentation for each language - Updated cookbook.yml, copilot-sdk README, and package.json
This commit is contained in:
@@ -61,3 +61,11 @@ cookbooks:
|
||||
- sessions
|
||||
- persistence
|
||||
- state-management
|
||||
- id: accessibility-report
|
||||
name: Accessibility Report
|
||||
description: Generate WCAG accessibility reports using the Playwright MCP server
|
||||
tags:
|
||||
- accessibility
|
||||
- playwright
|
||||
- mcp
|
||||
- wcag
|
||||
|
||||
@@ -12,6 +12,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
|
||||
- [Managing Local Files](dotnet/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
|
||||
- [PR Visualization](dotnet/pr-visualization.md): Generate interactive PR age charts using GitHub MCP Server.
|
||||
- [Persisting Sessions](dotnet/persisting-sessions.md): Save and resume sessions across restarts.
|
||||
- [Accessibility Report](dotnet/accessibility-report.md): Generate WCAG accessibility reports using the Playwright MCP server.
|
||||
|
||||
### Node.js / TypeScript
|
||||
|
||||
@@ -21,6 +22,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
|
||||
- [Managing Local Files](nodejs/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
|
||||
- [PR Visualization](nodejs/pr-visualization.md): Generate interactive PR age charts using GitHub MCP Server.
|
||||
- [Persisting Sessions](nodejs/persisting-sessions.md): Save and resume sessions across restarts.
|
||||
- [Accessibility Report](nodejs/accessibility-report.md): Generate WCAG accessibility reports using the Playwright MCP server.
|
||||
|
||||
### Python
|
||||
|
||||
@@ -30,6 +32,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
|
||||
- [Managing Local Files](python/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
|
||||
- [PR Visualization](python/pr-visualization.md): Generate interactive PR age charts using GitHub MCP Server.
|
||||
- [Persisting Sessions](python/persisting-sessions.md): Save and resume sessions across restarts.
|
||||
- [Accessibility Report](python/accessibility-report.md): Generate WCAG accessibility reports using the Playwright MCP server.
|
||||
|
||||
### Go
|
||||
|
||||
@@ -39,6 +42,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
|
||||
- [Managing Local Files](go/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
|
||||
- [PR Visualization](go/pr-visualization.md): Generate interactive PR age charts using GitHub MCP Server.
|
||||
- [Persisting Sessions](go/persisting-sessions.md): Save and resume sessions across restarts.
|
||||
- [Accessibility Report](go/accessibility-report.md): Generate WCAG accessibility reports using the Playwright MCP server.
|
||||
|
||||
## How to Use
|
||||
|
||||
@@ -87,4 +91,4 @@ go run <filename>.go
|
||||
|
||||
## Status
|
||||
|
||||
Cookbook structure is complete with 6 recipes across all 4 supported languages. Each recipe includes both markdown documentation and runnable examples.
|
||||
Cookbook structure is complete with 7 recipes across all 4 supported languages. Each recipe includes both markdown documentation and runnable examples.
|
||||
|
||||
287
cookbook/copilot-sdk/dotnet/accessibility-report.md
Normal file
287
cookbook/copilot-sdk/dotnet/accessibility-report.md
Normal file
@@ -0,0 +1,287 @@
|
||||
# Generating Accessibility Reports
|
||||
|
||||
Build a CLI tool that analyzes web page accessibility using the Playwright MCP server and generates detailed WCAG-compliant reports with optional test generation.
|
||||
|
||||
> **Runnable example:** [recipe/accessibility-report.cs](recipe/accessibility-report.cs)
|
||||
>
|
||||
> ```bash
|
||||
> dotnet run recipe/accessibility-report.cs
|
||||
> ```
|
||||
|
||||
## Example scenario
|
||||
|
||||
You want to audit a website's accessibility compliance. This tool navigates to a URL using Playwright, captures an accessibility snapshot, and produces a structured report covering WCAG criteria like landmarks, heading hierarchy, focus management, and touch targets. It can also generate Playwright test files to automate future accessibility checks.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
dotnet add package GitHub.Copilot.SDK
|
||||
```
|
||||
|
||||
You also need `npx` available (Node.js installed) for the Playwright MCP server.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
dotnet run recipe/accessibility-report.cs
|
||||
# Enter a URL when prompted
|
||||
```
|
||||
|
||||
## Full example: accessibility-report.cs
|
||||
|
||||
```csharp
|
||||
#:package GitHub.Copilot.SDK@*
|
||||
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
// Create and start client
|
||||
await using var client = new CopilotClient();
|
||||
await client.StartAsync();
|
||||
|
||||
Console.WriteLine("=== Accessibility Report Generator ===");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.Write("Enter URL to analyze: ");
|
||||
var url = Console.ReadLine()?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
{
|
||||
Console.WriteLine("No URL provided. Exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure URL has a scheme
|
||||
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
|
||||
{
|
||||
url = "https://" + url;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\nAnalyzing: {url}");
|
||||
Console.WriteLine("Please wait...\n");
|
||||
|
||||
// Create a session with Playwright MCP server
|
||||
await using var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "claude-opus-4.6",
|
||||
Streaming = true,
|
||||
McpServers = new Dictionary<string, object>()
|
||||
{
|
||||
["playwright"] =
|
||||
new McpLocalServerConfig
|
||||
{
|
||||
Type = "local",
|
||||
Command = "npx",
|
||||
Args = ["@playwright/mcp@latest"],
|
||||
Tools = ["*"]
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Wait for response using session.idle event
|
||||
var done = new TaskCompletionSource();
|
||||
|
||||
session.On(evt =>
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case AssistantMessageDeltaEvent delta:
|
||||
Console.Write(delta.Data.DeltaContent);
|
||||
break;
|
||||
case SessionIdleEvent:
|
||||
done.TrySetResult();
|
||||
break;
|
||||
case SessionErrorEvent error:
|
||||
Console.WriteLine($"\nError: {error.Data.Message}");
|
||||
done.TrySetResult();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
var prompt = $"""
|
||||
Use the Playwright MCP server to analyze the accessibility of this webpage: {url}
|
||||
|
||||
Please:
|
||||
1. Navigate to the URL using playwright-browser_navigate
|
||||
2. Take an accessibility snapshot using playwright-browser_snapshot
|
||||
3. Analyze the snapshot and provide a detailed accessibility report
|
||||
|
||||
Format the report EXACTLY like this structure with emoji indicators:
|
||||
|
||||
📊 Accessibility Report: [Page Title] (domain.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en-US" properly set |
|
||||
| Page Title | ✅ Pass | "[Title]" is descriptive |
|
||||
| Heading Hierarchy | ✅ Pass | Single H1, proper H2/H3 structure |
|
||||
| Images | ✅ Pass | All X images have alt text |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🔴 High | No <main> landmark | 1.3.1, 2.4.1 | Wrap main content in <main> element |
|
||||
| 🟡 Medium | Focus outlines disabled | 2.4.7 | Ensure visible :focus styles exist |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: X
|
||||
- Total Headings: X
|
||||
- Focusable Elements: X
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ❌, footer ✅
|
||||
|
||||
⚙️ Priority Recommendations
|
||||
...
|
||||
|
||||
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
|
||||
Include actual findings from the page analysis - don't just copy the example.
|
||||
""";
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = prompt });
|
||||
await done.Task;
|
||||
|
||||
Console.WriteLine("\n\n=== Report Complete ===\n");
|
||||
|
||||
// Prompt user for test generation
|
||||
Console.Write("Would you like to generate Playwright accessibility tests? (y/n): ");
|
||||
var generateTests = Console.ReadLine()?.Trim().ToLowerInvariant();
|
||||
|
||||
if (generateTests == "y" || generateTests == "yes")
|
||||
{
|
||||
// Reset for next interaction
|
||||
done = new TaskCompletionSource();
|
||||
|
||||
var detectLanguagePrompt = $"""
|
||||
Analyze the current working directory to detect the primary programming language used in this project.
|
||||
Respond with ONLY the detected language name and a brief explanation.
|
||||
If no project is detected, suggest "TypeScript" as the default for Playwright tests.
|
||||
""";
|
||||
|
||||
Console.WriteLine("\nDetecting project language...\n");
|
||||
await session.SendAsync(new MessageOptions { Prompt = detectLanguagePrompt });
|
||||
await done.Task;
|
||||
|
||||
Console.Write("\n\nConfirm language for tests (or enter a different one): ");
|
||||
var language = Console.ReadLine()?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(language))
|
||||
{
|
||||
language = "TypeScript";
|
||||
}
|
||||
|
||||
// Reset for test generation
|
||||
done = new TaskCompletionSource();
|
||||
|
||||
var testGenerationPrompt = $"""
|
||||
Based on the accessibility report you just generated for {url}, create Playwright accessibility tests in {language}.
|
||||
|
||||
The tests should:
|
||||
1. Verify all the accessibility checks from the report
|
||||
2. Test for the issues that were found (to ensure they get fixed)
|
||||
3. Include tests for landmarks, heading hierarchy, alt text, focus indicators, and more
|
||||
4. Use Playwright's accessibility testing features
|
||||
5. Include helpful comments explaining each test
|
||||
|
||||
Output the complete test file that can be saved and run.
|
||||
""";
|
||||
|
||||
Console.WriteLine("\nGenerating accessibility tests...\n");
|
||||
await session.SendAsync(new MessageOptions { Prompt = testGenerationPrompt });
|
||||
await done.Task;
|
||||
|
||||
Console.WriteLine("\n\n=== Tests Generated ===");
|
||||
}
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Playwright MCP server**: Configures a local MCP server running `@playwright/mcp` to provide browser automation tools
|
||||
2. **Streaming output**: Uses `Streaming = true` and `AssistantMessageDeltaEvent` for real-time token-by-token output
|
||||
3. **Accessibility snapshot**: Playwright's `browser_snapshot` tool captures the full accessibility tree of the page
|
||||
4. **Structured report**: The prompt engineers a consistent WCAG-aligned report format with emoji severity indicators
|
||||
5. **Test generation**: Optionally detects the project language and generates Playwright accessibility tests
|
||||
|
||||
## Key concepts
|
||||
|
||||
### MCP server configuration
|
||||
|
||||
The recipe configures a local MCP server that runs alongside the session:
|
||||
|
||||
```csharp
|
||||
McpServers = new Dictionary<string, object>()
|
||||
{
|
||||
["playwright"] = new McpLocalServerConfig
|
||||
{
|
||||
Type = "local",
|
||||
Command = "npx",
|
||||
Args = ["@playwright/mcp@latest"],
|
||||
Tools = ["*"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This gives the model access to Playwright browser tools like `browser_navigate`, `browser_snapshot`, and `browser_click`.
|
||||
|
||||
### Streaming with events
|
||||
|
||||
Unlike `SendAndWaitAsync`, this recipe uses streaming for real-time output:
|
||||
|
||||
```csharp
|
||||
session.On(evt =>
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case AssistantMessageDeltaEvent delta:
|
||||
Console.Write(delta.Data.DeltaContent); // Token-by-token
|
||||
break;
|
||||
case SessionIdleEvent:
|
||||
done.TrySetResult(); // Model finished
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Sample interaction
|
||||
|
||||
```
|
||||
=== Accessibility Report Generator ===
|
||||
|
||||
Enter URL to analyze: github.com
|
||||
|
||||
Analyzing: https://github.com
|
||||
Please wait...
|
||||
|
||||
📊 Accessibility Report: GitHub (github.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en" properly set |
|
||||
| Page Title | ✅ Pass | "GitHub" is recognizable |
|
||||
| Heading Hierarchy | ✅ Pass | Proper H1/H2 structure |
|
||||
| Images | ✅ Pass | All images have alt text |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🟡 Medium | Some links lack descriptive text | 2.4.4 | Add aria-label to icon-only links |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: 47
|
||||
- Total Headings: 8 (1× H1, proper hierarchy)
|
||||
- Focusable Elements: 52
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ✅, footer ✅
|
||||
|
||||
=== Report Complete ===
|
||||
|
||||
Would you like to generate Playwright accessibility tests? (y/n): y
|
||||
|
||||
Detecting project language...
|
||||
TypeScript detected (package.json found)
|
||||
|
||||
Confirm language for tests (or enter a different one):
|
||||
|
||||
Generating accessibility tests...
|
||||
[Generated test file output...]
|
||||
|
||||
=== Tests Generated ===
|
||||
```
|
||||
184
cookbook/copilot-sdk/dotnet/recipe/accessibility-report.cs
Normal file
184
cookbook/copilot-sdk/dotnet/recipe/accessibility-report.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
#:package GitHub.Copilot.SDK@*
|
||||
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
// Create and start client
|
||||
await using var client = new CopilotClient();
|
||||
await client.StartAsync();
|
||||
|
||||
Console.WriteLine("=== Accessibility Report Generator ===");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.Write("Enter URL to analyze: ");
|
||||
var url = Console.ReadLine()?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
{
|
||||
Console.WriteLine("No URL provided. Exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure URL has a scheme
|
||||
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
|
||||
{
|
||||
url = "https://" + url;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\nAnalyzing: {url}");
|
||||
Console.WriteLine("Please wait...\n");
|
||||
|
||||
// Create a session with Playwright MCP server
|
||||
await using var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "claude-opus-4.6",
|
||||
Streaming = true,
|
||||
McpServers = new Dictionary<string, object>()
|
||||
{
|
||||
["playwright"] =
|
||||
new McpLocalServerConfig
|
||||
{
|
||||
Type = "local",
|
||||
Command = "npx",
|
||||
Args = ["@playwright/mcp@latest"],
|
||||
Tools = ["*"]
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Wait for response using session.idle event
|
||||
var done = new TaskCompletionSource();
|
||||
|
||||
session.On(evt =>
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case AssistantMessageDeltaEvent delta:
|
||||
Console.Write(delta.Data.DeltaContent);
|
||||
break;
|
||||
case SessionIdleEvent:
|
||||
done.TrySetResult();
|
||||
break;
|
||||
case SessionErrorEvent error:
|
||||
Console.WriteLine($"\nError: {error.Data.Message}");
|
||||
done.TrySetResult();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
var prompt = $"""
|
||||
Use the Playwright MCP server to analyze the accessibility of this webpage: {url}
|
||||
|
||||
Please:
|
||||
1. Navigate to the URL using playwright-browser_navigate
|
||||
2. Take an accessibility snapshot using playwright-browser_snapshot
|
||||
3. Analyze the snapshot and provide a detailed accessibility report
|
||||
|
||||
Format the report EXACTLY like this structure with emoji indicators:
|
||||
|
||||
📊 Accessibility Report: [Page Title] (domain.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en-US" properly set |
|
||||
| Page Title | ✅ Pass | "[Title]" is descriptive |
|
||||
| Heading Hierarchy | ✅ Pass | Single H1, proper H2/H3 structure |
|
||||
| Images | ✅ Pass | All X images have alt text |
|
||||
| Viewport | ✅ Pass | Allows pinch-zoom (no user-scalable=no) |
|
||||
| Links | ✅ Pass | No ambiguous "click here" links |
|
||||
| Reduced Motion | ✅ Pass | Supports prefers-reduced-motion |
|
||||
| Autoplay Media | ✅ Pass | No autoplay audio/video |
|
||||
| Font Selector | ✅ Excellent | Includes OpenDyslexic option for dyslexia |
|
||||
| Dark/Light Mode | ✅ Excellent | User-controlled theme toggle |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🔴 High | No <main> landmark | 1.3.1, 2.4.1 | Wrap main content in <main> element |
|
||||
| 🔴 High | No skip navigation link | 2.4.1 | Add "Skip to content" link at top |
|
||||
| 🟡 Medium | Focus outlines disabled | 2.4.7 | Default outline is none - ensure visible :focus styles exist |
|
||||
| 🟡 Medium | Small touch targets | 2.5.8 | Navigation links are 37px tall (below 44px minimum) |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: X
|
||||
- Total Headings: X (1× H1, proper hierarchy)
|
||||
- Focusable Elements: X
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ❌, footer ✅
|
||||
|
||||
⚙️ Priority Recommendations
|
||||
- Add <main> landmark - Wrap page content in <main role="main"> for screen reader navigation
|
||||
- Add skip link - Hidden link at start: <a href="#main-content" class="skip-link">Skip to content</a>
|
||||
- Increase touch targets - Add padding to nav links and tags to meet 44×44px minimum
|
||||
- Verify focus styles - Test keyboard navigation; add visible :focus or :focus-visible outlines
|
||||
|
||||
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
|
||||
Include actual findings from the page analysis - don't just copy the example.
|
||||
""";
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = prompt });
|
||||
await done.Task;
|
||||
|
||||
Console.WriteLine("\n\n=== Report Complete ===\n");
|
||||
|
||||
// Prompt user for test generation
|
||||
Console.Write("Would you like to generate Playwright accessibility tests? (y/n): ");
|
||||
var generateTests = Console.ReadLine()?.Trim().ToLowerInvariant();
|
||||
|
||||
if (generateTests == "y" || generateTests == "yes")
|
||||
{
|
||||
// Reset for next interaction
|
||||
done = new TaskCompletionSource();
|
||||
|
||||
var detectLanguagePrompt = $"""
|
||||
Analyze the current working directory to detect the primary programming language used in this project.
|
||||
Look for project files like package.json, *.csproj, pom.xml, requirements.txt, go.mod, etc.
|
||||
|
||||
Respond with ONLY the detected language name (e.g., "TypeScript", "JavaScript", "C#", "Python", "Java")
|
||||
and a brief explanation of why you detected it.
|
||||
If no project is detected, suggest "TypeScript" as the default for Playwright tests.
|
||||
""";
|
||||
|
||||
Console.WriteLine("\nDetecting project language...\n");
|
||||
await session.SendAsync(new MessageOptions { Prompt = detectLanguagePrompt });
|
||||
await done.Task;
|
||||
|
||||
Console.Write("\n\nConfirm language for tests (or enter a different one): ");
|
||||
var language = Console.ReadLine()?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(language))
|
||||
{
|
||||
language = "TypeScript";
|
||||
}
|
||||
|
||||
// Reset for test generation
|
||||
done = new TaskCompletionSource();
|
||||
|
||||
var testGenerationPrompt = $"""
|
||||
Based on the accessibility report you just generated for {url}, create Playwright accessibility tests in {language}.
|
||||
|
||||
The tests should:
|
||||
1. Verify all the accessibility checks from the report
|
||||
2. Test for the issues that were found (to ensure they get fixed)
|
||||
3. Include tests for:
|
||||
- Page has proper lang attribute
|
||||
- Page has descriptive title
|
||||
- Heading hierarchy is correct (single H1, proper nesting)
|
||||
- All images have alt text
|
||||
- No autoplay media
|
||||
- Landmark regions exist (banner, nav, main, footer)
|
||||
- Skip navigation link exists and works
|
||||
- Focus indicators are visible
|
||||
- Touch targets meet minimum size requirements
|
||||
4. Use Playwright's accessibility testing features
|
||||
5. Include helpful comments explaining each test
|
||||
|
||||
Output the complete test file that can be saved and run.
|
||||
Use the Playwright MCP server tools if you need to verify any page details.
|
||||
""";
|
||||
|
||||
Console.WriteLine("\nGenerating accessibility tests...\n");
|
||||
await session.SendAsync(new MessageOptions { Prompt = testGenerationPrompt });
|
||||
await done.Task;
|
||||
|
||||
Console.WriteLine("\n\n=== Tests Generated ===");
|
||||
}
|
||||
291
cookbook/copilot-sdk/go/accessibility-report.md
Normal file
291
cookbook/copilot-sdk/go/accessibility-report.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# Generating Accessibility Reports
|
||||
|
||||
Build a CLI tool that analyzes web page accessibility using the Playwright MCP server and generates detailed WCAG-compliant reports with optional test generation.
|
||||
|
||||
> **Runnable example:** [recipe/accessibility-report.go](recipe/accessibility-report.go)
|
||||
>
|
||||
> ```bash
|
||||
> go run recipe/accessibility-report.go
|
||||
> ```
|
||||
|
||||
## Example scenario
|
||||
|
||||
You want to audit a website's accessibility compliance. This tool navigates to a URL using Playwright, captures an accessibility snapshot, and produces a structured report covering WCAG criteria like landmarks, heading hierarchy, focus management, and touch targets. It can also generate Playwright test files to automate future accessibility checks.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
go get github.com/github/copilot-sdk/go
|
||||
```
|
||||
|
||||
You also need `npx` available (Node.js installed) for the Playwright MCP server.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
go run accessibility-report.go
|
||||
# Enter a URL when prompted
|
||||
```
|
||||
|
||||
## Full example: accessibility-report.go
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
copilot "github.com/github/copilot-sdk/go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Println("=== Accessibility Report Generator ===")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Enter URL to analyze: ")
|
||||
url, _ := reader.ReadString('\n')
|
||||
url = strings.TrimSpace(url)
|
||||
|
||||
if url == "" {
|
||||
fmt.Println("No URL provided. Exiting.")
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure URL has a scheme
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
url = "https://" + url
|
||||
}
|
||||
|
||||
fmt.Printf("\nAnalyzing: %s\n", url)
|
||||
fmt.Println("Please wait...\n")
|
||||
|
||||
// Create Copilot client with Playwright MCP server
|
||||
client := copilot.NewClient(nil)
|
||||
|
||||
if err := client.Start(ctx); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Stop()
|
||||
|
||||
streaming := true
|
||||
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
|
||||
Model: "claude-opus-4.6",
|
||||
Streaming: &streaming,
|
||||
McpServers: map[string]interface{}{
|
||||
"playwright": map[string]interface{}{
|
||||
"type": "local",
|
||||
"command": "npx",
|
||||
"args": []string{"@playwright/mcp@latest"},
|
||||
"tools": []string{"*"},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer session.Destroy()
|
||||
|
||||
// Set up streaming event handling
|
||||
done := make(chan struct{}, 1)
|
||||
|
||||
session.On(func(event copilot.SessionEvent) {
|
||||
switch event.Type {
|
||||
case "assistant.message.delta":
|
||||
if event.Data.DeltaContent != nil {
|
||||
fmt.Print(*event.Data.DeltaContent)
|
||||
}
|
||||
case "session.idle":
|
||||
select {
|
||||
case done <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
case "session.error":
|
||||
if event.Data.Message != nil {
|
||||
fmt.Printf("\nError: %s\n", *event.Data.Message)
|
||||
}
|
||||
select {
|
||||
case done <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
prompt := fmt.Sprintf(`
|
||||
Use the Playwright MCP server to analyze the accessibility of this webpage: %s
|
||||
|
||||
Please:
|
||||
1. Navigate to the URL using playwright-browser_navigate
|
||||
2. Take an accessibility snapshot using playwright-browser_snapshot
|
||||
3. Analyze the snapshot and provide a detailed accessibility report
|
||||
|
||||
Format the report with emoji indicators:
|
||||
- 📊 Accessibility Report header
|
||||
- ✅ What's Working Well (table with Category, Status, Details)
|
||||
- ⚠️ Issues Found (table with Severity, Issue, WCAG Criterion, Recommendation)
|
||||
- 📋 Stats Summary (links, headings, focusable elements, landmarks)
|
||||
- ⚙️ Priority Recommendations
|
||||
|
||||
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
|
||||
Include actual findings from the page analysis.
|
||||
`, url)
|
||||
|
||||
if _, err := session.Send(ctx, copilot.MessageOptions{Prompt: prompt}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
|
||||
fmt.Println("\n\n=== Report Complete ===\n")
|
||||
|
||||
// Prompt user for test generation
|
||||
fmt.Print("Would you like to generate Playwright accessibility tests? (y/n): ")
|
||||
generateTests, _ := reader.ReadString('\n')
|
||||
generateTests = strings.TrimSpace(strings.ToLower(generateTests))
|
||||
|
||||
if generateTests == "y" || generateTests == "yes" {
|
||||
detectLanguagePrompt := `
|
||||
Analyze the current working directory to detect the primary programming language.
|
||||
Respond with ONLY the detected language name and a brief explanation.
|
||||
If no project is detected, suggest "TypeScript" as the default.
|
||||
`
|
||||
|
||||
fmt.Println("\nDetecting project language...\n")
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
}
|
||||
if _, err := session.Send(ctx, copilot.MessageOptions{Prompt: detectLanguagePrompt}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
|
||||
fmt.Print("\n\nConfirm language for tests (or enter a different one): ")
|
||||
language, _ := reader.ReadString('\n')
|
||||
language = strings.TrimSpace(language)
|
||||
if language == "" {
|
||||
language = "TypeScript"
|
||||
}
|
||||
|
||||
testGenerationPrompt := fmt.Sprintf(`
|
||||
Based on the accessibility report you just generated for %s,
|
||||
create Playwright accessibility tests in %s.
|
||||
|
||||
Include tests for: lang attribute, title, heading hierarchy, alt text,
|
||||
landmarks, skip navigation, focus indicators, and touch targets.
|
||||
Use Playwright's accessibility testing features with helpful comments.
|
||||
Output the complete test file.
|
||||
`, url, language)
|
||||
|
||||
fmt.Println("\nGenerating accessibility tests...\n")
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
}
|
||||
if _, err := session.Send(ctx, copilot.MessageOptions{Prompt: testGenerationPrompt}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
|
||||
fmt.Println("\n\n=== Tests Generated ===")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Playwright MCP server**: Configures a local MCP server running `@playwright/mcp` to provide browser automation tools
|
||||
2. **Streaming output**: Uses `Streaming: &streaming` and `assistant.message.delta` events for real-time token-by-token output
|
||||
3. **Accessibility snapshot**: Playwright's `browser_snapshot` tool captures the full accessibility tree of the page
|
||||
4. **Structured report**: The prompt engineers a consistent WCAG-aligned report format with emoji severity indicators
|
||||
5. **Test generation**: Optionally detects the project language and generates Playwright accessibility tests
|
||||
|
||||
## Key concepts
|
||||
|
||||
### MCP server configuration
|
||||
|
||||
The recipe configures a local MCP server that runs alongside the session:
|
||||
|
||||
```go
|
||||
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
|
||||
McpServers: map[string]interface{}{
|
||||
"playwright": map[string]interface{}{
|
||||
"type": "local",
|
||||
"command": "npx",
|
||||
"args": []string{"@playwright/mcp@latest"},
|
||||
"tools": []string{"*"},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
This gives the model access to Playwright browser tools like `browser_navigate`, `browser_snapshot`, and `browser_click`.
|
||||
|
||||
### Streaming with events
|
||||
|
||||
Unlike `SendAndWait`, this recipe uses streaming for real-time output:
|
||||
|
||||
```go
|
||||
session.On(func(event copilot.SessionEvent) {
|
||||
switch event.Type {
|
||||
case "assistant.message.delta":
|
||||
if event.Data.DeltaContent != nil {
|
||||
fmt.Print(*event.Data.DeltaContent)
|
||||
}
|
||||
case "session.idle":
|
||||
done <- struct{}{}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Sample interaction
|
||||
|
||||
```
|
||||
=== Accessibility Report Generator ===
|
||||
|
||||
Enter URL to analyze: github.com
|
||||
|
||||
Analyzing: https://github.com
|
||||
Please wait...
|
||||
|
||||
📊 Accessibility Report: GitHub (github.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en" properly set |
|
||||
| Page Title | ✅ Pass | "GitHub" is recognizable |
|
||||
| Heading Hierarchy | ✅ Pass | Proper H1/H2 structure |
|
||||
| Images | ✅ Pass | All images have alt text |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🟡 Medium | Some links lack descriptive text | 2.4.4 | Add aria-label to icon-only links |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: 47
|
||||
- Total Headings: 8 (1× H1, proper hierarchy)
|
||||
- Focusable Elements: 52
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ✅, footer ✅
|
||||
|
||||
=== Report Complete ===
|
||||
|
||||
Would you like to generate Playwright accessibility tests? (y/n): y
|
||||
|
||||
Detecting project language...
|
||||
TypeScript detected (package.json found)
|
||||
|
||||
Confirm language for tests (or enter a different one):
|
||||
|
||||
Generating accessibility tests...
|
||||
[Generated test file output...]
|
||||
|
||||
=== Tests Generated ===
|
||||
```
|
||||
213
cookbook/copilot-sdk/go/recipe/accessibility-report.go
Normal file
213
cookbook/copilot-sdk/go/recipe/accessibility-report.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
copilot "github.com/github/copilot-sdk/go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Println("=== Accessibility Report Generator ===")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Enter URL to analyze: ")
|
||||
url, _ := reader.ReadString('\n')
|
||||
url = strings.TrimSpace(url)
|
||||
|
||||
if url == "" {
|
||||
fmt.Println("No URL provided. Exiting.")
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure URL has a scheme
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
url = "https://" + url
|
||||
}
|
||||
|
||||
fmt.Printf("\nAnalyzing: %s\n", url)
|
||||
fmt.Println("Please wait...\n")
|
||||
|
||||
// Create Copilot client with Playwright MCP server
|
||||
client := copilot.NewClient(nil)
|
||||
|
||||
if err := client.Start(ctx); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Stop()
|
||||
|
||||
streaming := true
|
||||
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
|
||||
Model: "claude-opus-4.6",
|
||||
Streaming: &streaming,
|
||||
McpServers: map[string]interface{}{
|
||||
"playwright": map[string]interface{}{
|
||||
"type": "local",
|
||||
"command": "npx",
|
||||
"args": []string{"@playwright/mcp@latest"},
|
||||
"tools": []string{"*"},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer session.Destroy()
|
||||
|
||||
// Set up streaming event handling
|
||||
done := make(chan struct{}, 1)
|
||||
|
||||
session.On(func(event copilot.SessionEvent) {
|
||||
switch event.Type {
|
||||
case "assistant.message.delta":
|
||||
if event.Data.DeltaContent != nil {
|
||||
fmt.Print(*event.Data.DeltaContent)
|
||||
}
|
||||
case "session.idle":
|
||||
select {
|
||||
case done <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
case "session.error":
|
||||
if event.Data.Message != nil {
|
||||
fmt.Printf("\nError: %s\n", *event.Data.Message)
|
||||
}
|
||||
select {
|
||||
case done <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
prompt := fmt.Sprintf(`
|
||||
Use the Playwright MCP server to analyze the accessibility of this webpage: %s
|
||||
|
||||
Please:
|
||||
1. Navigate to the URL using playwright-browser_navigate
|
||||
2. Take an accessibility snapshot using playwright-browser_snapshot
|
||||
3. Analyze the snapshot and provide a detailed accessibility report
|
||||
|
||||
Format the report EXACTLY like this structure with emoji indicators:
|
||||
|
||||
📊 Accessibility Report: [Page Title] (domain.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en-US" properly set |
|
||||
| Page Title | ✅ Pass | "[Title]" is descriptive |
|
||||
| Heading Hierarchy | ✅ Pass | Single H1, proper H2/H3 structure |
|
||||
| Images | ✅ Pass | All X images have alt text |
|
||||
| Viewport | ✅ Pass | Allows pinch-zoom (no user-scalable=no) |
|
||||
| Links | ✅ Pass | No ambiguous "click here" links |
|
||||
| Reduced Motion | ✅ Pass | Supports prefers-reduced-motion |
|
||||
| Autoplay Media | ✅ Pass | No autoplay audio/video |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🔴 High | No <main> landmark | 1.3.1, 2.4.1 | Wrap main content in <main> element |
|
||||
| 🔴 High | No skip navigation link | 2.4.1 | Add "Skip to content" link at top |
|
||||
| 🟡 Medium | Focus outlines disabled | 2.4.7 | Default outline is none - ensure visible :focus styles exist |
|
||||
| 🟡 Medium | Small touch targets | 2.5.8 | Navigation links are 37px tall (below 44px minimum) |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: X
|
||||
- Total Headings: X (1× H1, proper hierarchy)
|
||||
- Focusable Elements: X
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ❌, footer ✅
|
||||
|
||||
⚙️ Priority Recommendations
|
||||
- Add <main> landmark - Wrap page content in <main role="main"> for screen reader navigation
|
||||
- Add skip link - Hidden link at start: <a href="#main-content" class="skip-link">Skip to content</a>
|
||||
- Increase touch targets - Add padding to nav links and tags to meet 44×44px minimum
|
||||
- Verify focus styles - Test keyboard navigation; add visible :focus or :focus-visible outlines
|
||||
|
||||
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
|
||||
Include actual findings from the page analysis - don't just copy the example.
|
||||
`, url)
|
||||
|
||||
if _, err := session.Send(ctx, copilot.MessageOptions{Prompt: prompt}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
|
||||
fmt.Println("\n\n=== Report Complete ===\n")
|
||||
|
||||
// Prompt user for test generation
|
||||
fmt.Print("Would you like to generate Playwright accessibility tests? (y/n): ")
|
||||
generateTests, _ := reader.ReadString('\n')
|
||||
generateTests = strings.TrimSpace(strings.ToLower(generateTests))
|
||||
|
||||
if generateTests == "y" || generateTests == "yes" {
|
||||
detectLanguagePrompt := `
|
||||
Analyze the current working directory to detect the primary programming language used in this project.
|
||||
Look for project files like package.json, *.csproj, pom.xml, requirements.txt, go.mod, etc.
|
||||
|
||||
Respond with ONLY the detected language name (e.g., "TypeScript", "JavaScript", "C#", "Python", "Java")
|
||||
and a brief explanation of why you detected it.
|
||||
If no project is detected, suggest "TypeScript" as the default for Playwright tests.
|
||||
`
|
||||
|
||||
fmt.Println("\nDetecting project language...\n")
|
||||
// Drain the previous done signal
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
}
|
||||
if _, err := session.Send(ctx, copilot.MessageOptions{Prompt: detectLanguagePrompt}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
|
||||
fmt.Print("\n\nConfirm language for tests (or enter a different one): ")
|
||||
language, _ := reader.ReadString('\n')
|
||||
language = strings.TrimSpace(language)
|
||||
if language == "" {
|
||||
language = "TypeScript"
|
||||
}
|
||||
|
||||
testGenerationPrompt := fmt.Sprintf(`
|
||||
Based on the accessibility report you just generated for %s, create Playwright accessibility tests in %s.
|
||||
|
||||
The tests should:
|
||||
1. Verify all the accessibility checks from the report
|
||||
2. Test for the issues that were found (to ensure they get fixed)
|
||||
3. Include tests for:
|
||||
- Page has proper lang attribute
|
||||
- Page has descriptive title
|
||||
- Heading hierarchy is correct (single H1, proper nesting)
|
||||
- All images have alt text
|
||||
- No autoplay media
|
||||
- Landmark regions exist (banner, nav, main, footer)
|
||||
- Skip navigation link exists and works
|
||||
- Focus indicators are visible
|
||||
- Touch targets meet minimum size requirements
|
||||
4. Use Playwright's accessibility testing features
|
||||
5. Include helpful comments explaining each test
|
||||
|
||||
Output the complete test file that can be saved and run.
|
||||
Use the Playwright MCP server tools if you need to verify any page details.
|
||||
`, url, language)
|
||||
|
||||
fmt.Println("\nGenerating accessibility tests...\n")
|
||||
// Drain the previous done signal
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
}
|
||||
if _, err := session.Send(ctx, copilot.MessageOptions{Prompt: testGenerationPrompt}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
|
||||
fmt.Println("\n\n=== Tests Generated ===")
|
||||
}
|
||||
}
|
||||
265
cookbook/copilot-sdk/nodejs/accessibility-report.md
Normal file
265
cookbook/copilot-sdk/nodejs/accessibility-report.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Generating Accessibility Reports
|
||||
|
||||
Build a CLI tool that analyzes web page accessibility using the Playwright MCP server and generates detailed WCAG-compliant reports with optional test generation.
|
||||
|
||||
> **Runnable example:** [recipe/accessibility-report.ts](recipe/accessibility-report.ts)
|
||||
>
|
||||
> ```bash
|
||||
> cd recipe && npm install
|
||||
> npx tsx accessibility-report.ts
|
||||
> # or: npm run accessibility-report
|
||||
> ```
|
||||
|
||||
## Example scenario
|
||||
|
||||
You want to audit a website's accessibility compliance. This tool navigates to a URL using Playwright, captures an accessibility snapshot, and produces a structured report covering WCAG criteria like landmarks, heading hierarchy, focus management, and touch targets. It can also generate Playwright test files to automate future accessibility checks.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
npm install @github/copilot-sdk
|
||||
npm install -D typescript tsx @types/node
|
||||
```
|
||||
|
||||
You also need `npx` available (Node.js installed) for the Playwright MCP server.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
npx tsx accessibility-report.ts
|
||||
# Enter a URL when prompted
|
||||
```
|
||||
|
||||
## Full example: accessibility-report.ts
|
||||
|
||||
```typescript
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
import { CopilotClient } from "@github/copilot-sdk";
|
||||
import * as readline from "node:readline";
|
||||
|
||||
// ============================================================================
|
||||
// Main Application
|
||||
// ============================================================================
|
||||
|
||||
async function main() {
|
||||
console.log("=== Accessibility Report Generator ===\n");
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
const askQuestion = (query: string): Promise<string> =>
|
||||
new Promise((resolve) => rl.question(query, (answer) => resolve(answer.trim())));
|
||||
|
||||
let url = await askQuestion("Enter URL to analyze: ");
|
||||
|
||||
if (!url) {
|
||||
console.log("No URL provided. Exiting.");
|
||||
rl.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure URL has a scheme
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
url = "https://" + url;
|
||||
}
|
||||
|
||||
console.log(`\nAnalyzing: ${url}`);
|
||||
console.log("Please wait...\n");
|
||||
|
||||
// Create Copilot client with Playwright MCP server
|
||||
const client = new CopilotClient();
|
||||
|
||||
const session = await client.createSession({
|
||||
model: "claude-opus-4.6",
|
||||
streaming: true,
|
||||
mcpServers: {
|
||||
playwright: {
|
||||
type: "local",
|
||||
command: "npx",
|
||||
args: ["@playwright/mcp@latest"],
|
||||
tools: ["*"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Set up streaming event handling
|
||||
let idleResolve: (() => void) | null = null;
|
||||
|
||||
session.on((event) => {
|
||||
if (event.type === "assistant.message.delta") {
|
||||
process.stdout.write(event.data.deltaContent ?? "");
|
||||
} else if (event.type === "session.idle") {
|
||||
idleResolve?.();
|
||||
} else if (event.type === "session.error") {
|
||||
console.error(`\nError: ${event.data.message}`);
|
||||
idleResolve?.();
|
||||
}
|
||||
});
|
||||
|
||||
const waitForIdle = (): Promise<void> =>
|
||||
new Promise((resolve) => {
|
||||
idleResolve = resolve;
|
||||
});
|
||||
|
||||
const prompt = `
|
||||
Use the Playwright MCP server to analyze the accessibility of this webpage: ${url}
|
||||
|
||||
Please:
|
||||
1. Navigate to the URL using playwright-browser_navigate
|
||||
2. Take an accessibility snapshot using playwright-browser_snapshot
|
||||
3. Analyze the snapshot and provide a detailed accessibility report
|
||||
|
||||
Format the report with emoji indicators:
|
||||
- 📊 Accessibility Report header
|
||||
- ✅ What's Working Well (table with Category, Status, Details)
|
||||
- ⚠️ Issues Found (table with Severity, Issue, WCAG Criterion, Recommendation)
|
||||
- 📋 Stats Summary (links, headings, focusable elements, landmarks)
|
||||
- ⚙️ Priority Recommendations
|
||||
|
||||
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
|
||||
Include actual findings from the page analysis.
|
||||
`;
|
||||
|
||||
let idle = waitForIdle();
|
||||
await session.send({ prompt });
|
||||
await idle;
|
||||
|
||||
console.log("\n\n=== Report Complete ===\n");
|
||||
|
||||
// Prompt user for test generation
|
||||
const generateTests = await askQuestion(
|
||||
"Would you like to generate Playwright accessibility tests? (y/n): "
|
||||
);
|
||||
|
||||
if (generateTests.toLowerCase() === "y" || generateTests.toLowerCase() === "yes") {
|
||||
const detectLanguagePrompt = `
|
||||
Analyze the current working directory to detect the primary programming language.
|
||||
Respond with ONLY the detected language name and a brief explanation.
|
||||
If no project is detected, suggest "TypeScript" as the default.
|
||||
`;
|
||||
|
||||
console.log("\nDetecting project language...\n");
|
||||
idle = waitForIdle();
|
||||
await session.send({ prompt: detectLanguagePrompt });
|
||||
await idle;
|
||||
|
||||
let language = await askQuestion("\n\nConfirm language for tests (or enter a different one): ");
|
||||
if (!language) language = "TypeScript";
|
||||
|
||||
const testGenerationPrompt = `
|
||||
Based on the accessibility report you just generated for ${url},
|
||||
create Playwright accessibility tests in ${language}.
|
||||
|
||||
Include tests for: lang attribute, title, heading hierarchy, alt text,
|
||||
landmarks, skip navigation, focus indicators, and touch targets.
|
||||
Use Playwright's accessibility testing features with helpful comments.
|
||||
Output the complete test file.
|
||||
`;
|
||||
|
||||
console.log("\nGenerating accessibility tests...\n");
|
||||
idle = waitForIdle();
|
||||
await session.send({ prompt: testGenerationPrompt });
|
||||
await idle;
|
||||
|
||||
console.log("\n\n=== Tests Generated ===");
|
||||
}
|
||||
|
||||
rl.close();
|
||||
await session.destroy();
|
||||
await client.stop();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Playwright MCP server**: Configures a local MCP server running `@playwright/mcp` to provide browser automation tools
|
||||
2. **Streaming output**: Uses `streaming: true` and `assistant.message.delta` events for real-time token-by-token output
|
||||
3. **Accessibility snapshot**: Playwright's `browser_snapshot` tool captures the full accessibility tree of the page
|
||||
4. **Structured report**: The prompt engineers a consistent WCAG-aligned report format with emoji severity indicators
|
||||
5. **Test generation**: Optionally detects the project language and generates Playwright accessibility tests
|
||||
|
||||
## Key concepts
|
||||
|
||||
### MCP server configuration
|
||||
|
||||
The recipe configures a local MCP server that runs alongside the session:
|
||||
|
||||
```typescript
|
||||
const session = await client.createSession({
|
||||
mcpServers: {
|
||||
playwright: {
|
||||
type: "local",
|
||||
command: "npx",
|
||||
args: ["@playwright/mcp@latest"],
|
||||
tools: ["*"],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
This gives the model access to Playwright browser tools like `browser_navigate`, `browser_snapshot`, and `browser_click`.
|
||||
|
||||
### Streaming with events
|
||||
|
||||
Unlike `sendAndWait`, this recipe uses streaming for real-time output:
|
||||
|
||||
```typescript
|
||||
session.on((event) => {
|
||||
if (event.type === "assistant.message.delta") {
|
||||
process.stdout.write(event.data.deltaContent ?? "");
|
||||
} else if (event.type === "session.idle") {
|
||||
idleResolve?.();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Sample interaction
|
||||
|
||||
```
|
||||
=== Accessibility Report Generator ===
|
||||
|
||||
Enter URL to analyze: github.com
|
||||
|
||||
Analyzing: https://github.com
|
||||
Please wait...
|
||||
|
||||
📊 Accessibility Report: GitHub (github.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en" properly set |
|
||||
| Page Title | ✅ Pass | "GitHub" is recognizable |
|
||||
| Heading Hierarchy | ✅ Pass | Proper H1/H2 structure |
|
||||
| Images | ✅ Pass | All images have alt text |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🟡 Medium | Some links lack descriptive text | 2.4.4 | Add aria-label to icon-only links |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: 47
|
||||
- Total Headings: 8 (1× H1, proper hierarchy)
|
||||
- Focusable Elements: 52
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ✅, footer ✅
|
||||
|
||||
=== Report Complete ===
|
||||
|
||||
Would you like to generate Playwright accessibility tests? (y/n): y
|
||||
|
||||
Detecting project language...
|
||||
TypeScript detected (package.json found)
|
||||
|
||||
Confirm language for tests (or enter a different one):
|
||||
|
||||
Generating accessibility tests...
|
||||
[Generated test file output...]
|
||||
|
||||
=== Tests Generated ===
|
||||
```
|
||||
187
cookbook/copilot-sdk/nodejs/recipe/accessibility-report.ts
Normal file
187
cookbook/copilot-sdk/nodejs/recipe/accessibility-report.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env tsx
|
||||
|
||||
import { CopilotClient } from "@github/copilot-sdk";
|
||||
import * as readline from "node:readline";
|
||||
|
||||
// ============================================================================
|
||||
// Main Application
|
||||
// ============================================================================
|
||||
|
||||
async function main() {
|
||||
console.log("=== Accessibility Report Generator ===\n");
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
const askQuestion = (query: string): Promise<string> =>
|
||||
new Promise((resolve) => rl.question(query, (answer) => resolve(answer.trim())));
|
||||
|
||||
let url = await askQuestion("Enter URL to analyze: ");
|
||||
|
||||
if (!url) {
|
||||
console.log("No URL provided. Exiting.");
|
||||
rl.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure URL has a scheme
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
url = "https://" + url;
|
||||
}
|
||||
|
||||
console.log(`\nAnalyzing: ${url}`);
|
||||
console.log("Please wait...\n");
|
||||
|
||||
// Create Copilot client with Playwright MCP server
|
||||
const client = new CopilotClient();
|
||||
|
||||
const session = await client.createSession({
|
||||
model: "claude-opus-4.6",
|
||||
streaming: true,
|
||||
mcpServers: {
|
||||
playwright: {
|
||||
type: "local",
|
||||
command: "npx",
|
||||
args: ["@playwright/mcp@latest"],
|
||||
tools: ["*"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Set up streaming event handling
|
||||
let idleResolve: (() => void) | null = null;
|
||||
|
||||
session.on((event) => {
|
||||
if (event.type === "assistant.message.delta") {
|
||||
process.stdout.write(event.data.deltaContent ?? "");
|
||||
} else if (event.type === "session.idle") {
|
||||
idleResolve?.();
|
||||
} else if (event.type === "session.error") {
|
||||
console.error(`\nError: ${event.data.message}`);
|
||||
idleResolve?.();
|
||||
}
|
||||
});
|
||||
|
||||
const waitForIdle = (): Promise<void> =>
|
||||
new Promise((resolve) => {
|
||||
idleResolve = resolve;
|
||||
});
|
||||
|
||||
const prompt = `
|
||||
Use the Playwright MCP server to analyze the accessibility of this webpage: ${url}
|
||||
|
||||
Please:
|
||||
1. Navigate to the URL using playwright-browser_navigate
|
||||
2. Take an accessibility snapshot using playwright-browser_snapshot
|
||||
3. Analyze the snapshot and provide a detailed accessibility report
|
||||
|
||||
Format the report EXACTLY like this structure with emoji indicators:
|
||||
|
||||
📊 Accessibility Report: [Page Title] (domain.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en-US" properly set |
|
||||
| Page Title | ✅ Pass | "[Title]" is descriptive |
|
||||
| Heading Hierarchy | ✅ Pass | Single H1, proper H2/H3 structure |
|
||||
| Images | ✅ Pass | All X images have alt text |
|
||||
| Viewport | ✅ Pass | Allows pinch-zoom (no user-scalable=no) |
|
||||
| Links | ✅ Pass | No ambiguous "click here" links |
|
||||
| Reduced Motion | ✅ Pass | Supports prefers-reduced-motion |
|
||||
| Autoplay Media | ✅ Pass | No autoplay audio/video |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🔴 High | No <main> landmark | 1.3.1, 2.4.1 | Wrap main content in <main> element |
|
||||
| 🔴 High | No skip navigation link | 2.4.1 | Add "Skip to content" link at top |
|
||||
| 🟡 Medium | Focus outlines disabled | 2.4.7 | Default outline is none - ensure visible :focus styles exist |
|
||||
| 🟡 Medium | Small touch targets | 2.5.8 | Navigation links are 37px tall (below 44px minimum) |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: X
|
||||
- Total Headings: X (1× H1, proper hierarchy)
|
||||
- Focusable Elements: X
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ❌, footer ✅
|
||||
|
||||
⚙️ Priority Recommendations
|
||||
- Add <main> landmark - Wrap page content in <main role="main"> for screen reader navigation
|
||||
- Add skip link - Hidden link at start: <a href="#main-content" class="skip-link">Skip to content</a>
|
||||
- Increase touch targets - Add padding to nav links and tags to meet 44×44px minimum
|
||||
- Verify focus styles - Test keyboard navigation; add visible :focus or :focus-visible outlines
|
||||
|
||||
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
|
||||
Include actual findings from the page analysis - don't just copy the example.
|
||||
`;
|
||||
|
||||
let idle = waitForIdle();
|
||||
await session.send({ prompt });
|
||||
await idle;
|
||||
|
||||
console.log("\n\n=== Report Complete ===\n");
|
||||
|
||||
// Prompt user for test generation
|
||||
const generateTests = await askQuestion(
|
||||
"Would you like to generate Playwright accessibility tests? (y/n): "
|
||||
);
|
||||
|
||||
if (generateTests.toLowerCase() === "y" || generateTests.toLowerCase() === "yes") {
|
||||
const detectLanguagePrompt = `
|
||||
Analyze the current working directory to detect the primary programming language used in this project.
|
||||
Look for project files like package.json, *.csproj, pom.xml, requirements.txt, go.mod, etc.
|
||||
|
||||
Respond with ONLY the detected language name (e.g., "TypeScript", "JavaScript", "C#", "Python", "Java")
|
||||
and a brief explanation of why you detected it.
|
||||
If no project is detected, suggest "TypeScript" as the default for Playwright tests.
|
||||
`;
|
||||
|
||||
console.log("\nDetecting project language...\n");
|
||||
idle = waitForIdle();
|
||||
await session.send({ prompt: detectLanguagePrompt });
|
||||
await idle;
|
||||
|
||||
let language = await askQuestion("\n\nConfirm language for tests (or enter a different one): ");
|
||||
if (!language) {
|
||||
language = "TypeScript";
|
||||
}
|
||||
|
||||
const testGenerationPrompt = `
|
||||
Based on the accessibility report you just generated for ${url}, create Playwright accessibility tests in ${language}.
|
||||
|
||||
The tests should:
|
||||
1. Verify all the accessibility checks from the report
|
||||
2. Test for the issues that were found (to ensure they get fixed)
|
||||
3. Include tests for:
|
||||
- Page has proper lang attribute
|
||||
- Page has descriptive title
|
||||
- Heading hierarchy is correct (single H1, proper nesting)
|
||||
- All images have alt text
|
||||
- No autoplay media
|
||||
- Landmark regions exist (banner, nav, main, footer)
|
||||
- Skip navigation link exists and works
|
||||
- Focus indicators are visible
|
||||
- Touch targets meet minimum size requirements
|
||||
4. Use Playwright's accessibility testing features
|
||||
5. Include helpful comments explaining each test
|
||||
|
||||
Output the complete test file that can be saved and run.
|
||||
Use the Playwright MCP server tools if you need to verify any page details.
|
||||
`;
|
||||
|
||||
console.log("\nGenerating accessibility tests...\n");
|
||||
idle = waitForIdle();
|
||||
await session.send({ prompt: testGenerationPrompt });
|
||||
await idle;
|
||||
|
||||
console.log("\n\n=== Tests Generated ===");
|
||||
}
|
||||
|
||||
rl.close();
|
||||
await session.destroy();
|
||||
await client.stop();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -8,7 +8,8 @@
|
||||
"multiple-sessions": "tsx multiple-sessions.ts",
|
||||
"managing-local-files": "tsx managing-local-files.ts",
|
||||
"pr-visualization": "tsx pr-visualization.ts",
|
||||
"persisting-sessions": "tsx persisting-sessions.ts"
|
||||
"persisting-sessions": "tsx persisting-sessions.ts",
|
||||
"accessibility-report": "tsx accessibility-report.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@github/copilot-sdk": "*"
|
||||
|
||||
253
cookbook/copilot-sdk/python/accessibility-report.md
Normal file
253
cookbook/copilot-sdk/python/accessibility-report.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# Generating Accessibility Reports
|
||||
|
||||
Build a CLI tool that analyzes web page accessibility using the Playwright MCP server and generates detailed WCAG-compliant reports with optional test generation.
|
||||
|
||||
> **Runnable example:** [recipe/accessibility_report.py](recipe/accessibility_report.py)
|
||||
>
|
||||
> ```bash
|
||||
> cd recipe && pip install -r requirements.txt
|
||||
> python accessibility_report.py
|
||||
> ```
|
||||
|
||||
## Example scenario
|
||||
|
||||
You want to audit a website's accessibility compliance. This tool navigates to a URL using Playwright, captures an accessibility snapshot, and produces a structured report covering WCAG criteria like landmarks, heading hierarchy, focus management, and touch targets. It can also generate Playwright test files to automate future accessibility checks.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
pip install github-copilot-sdk
|
||||
```
|
||||
|
||||
You also need `npx` available (Node.js installed) for the Playwright MCP server.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python accessibility_report.py
|
||||
# Enter a URL when prompted
|
||||
```
|
||||
|
||||
## Full example: accessibility_report.py
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
from copilot import (
|
||||
CopilotClient, SessionConfig, MessageOptions,
|
||||
SessionEvent, SessionEventType,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# Main Application
|
||||
# ============================================================================
|
||||
|
||||
async def main():
|
||||
print("=== Accessibility Report Generator ===\n")
|
||||
|
||||
url = input("Enter URL to analyze: ").strip()
|
||||
|
||||
if not url:
|
||||
print("No URL provided. Exiting.")
|
||||
return
|
||||
|
||||
# Ensure URL has a scheme
|
||||
if not url.startswith("http://") and not url.startswith("https://"):
|
||||
url = "https://" + url
|
||||
|
||||
print(f"\nAnalyzing: {url}")
|
||||
print("Please wait...\n")
|
||||
|
||||
# Create Copilot client with Playwright MCP server
|
||||
client = CopilotClient()
|
||||
await client.start()
|
||||
|
||||
session = await client.create_session(SessionConfig(
|
||||
model="claude-opus-4.6",
|
||||
streaming=True,
|
||||
mcp_servers={
|
||||
"playwright": {
|
||||
"type": "local",
|
||||
"command": "npx",
|
||||
"args": ["@playwright/mcp@latest"],
|
||||
"tools": ["*"],
|
||||
}
|
||||
},
|
||||
))
|
||||
|
||||
done = asyncio.Event()
|
||||
|
||||
# Set up streaming event handling
|
||||
def handle_event(event: SessionEvent):
|
||||
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
|
||||
print(event.data.delta_content or "", end="", flush=True)
|
||||
elif event.type.value == "session.idle":
|
||||
done.set()
|
||||
elif event.type.value == "session.error":
|
||||
print(f"\nError: {event.data.message}")
|
||||
done.set()
|
||||
|
||||
session.on(handle_event)
|
||||
|
||||
prompt = f"""
|
||||
Use the Playwright MCP server to analyze the accessibility of this webpage: {url}
|
||||
|
||||
Please:
|
||||
1. Navigate to the URL using playwright-browser_navigate
|
||||
2. Take an accessibility snapshot using playwright-browser_snapshot
|
||||
3. Analyze the snapshot and provide a detailed accessibility report
|
||||
|
||||
Format the report with emoji indicators:
|
||||
- 📊 Accessibility Report header
|
||||
- ✅ What's Working Well (table with Category, Status, Details)
|
||||
- ⚠️ Issues Found (table with Severity, Issue, WCAG Criterion, Recommendation)
|
||||
- 📋 Stats Summary (links, headings, focusable elements, landmarks)
|
||||
- ⚙️ Priority Recommendations
|
||||
|
||||
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
|
||||
Include actual findings from the page analysis.
|
||||
"""
|
||||
|
||||
await session.send(MessageOptions(prompt=prompt))
|
||||
await done.wait()
|
||||
|
||||
print("\n\n=== Report Complete ===\n")
|
||||
|
||||
# Prompt user for test generation
|
||||
generate_tests = input(
|
||||
"Would you like to generate Playwright accessibility tests? (y/n): "
|
||||
).strip().lower()
|
||||
|
||||
if generate_tests in ("y", "yes"):
|
||||
done.clear()
|
||||
|
||||
detect_language_prompt = """
|
||||
Analyze the current working directory to detect the primary programming language.
|
||||
Respond with ONLY the detected language name and a brief explanation.
|
||||
If no project is detected, suggest "TypeScript" as the default.
|
||||
"""
|
||||
|
||||
print("\nDetecting project language...\n")
|
||||
await session.send(MessageOptions(prompt=detect_language_prompt))
|
||||
await done.wait()
|
||||
|
||||
language = input(
|
||||
"\n\nConfirm language for tests (or enter a different one): "
|
||||
).strip()
|
||||
if not language:
|
||||
language = "TypeScript"
|
||||
|
||||
done.clear()
|
||||
|
||||
test_generation_prompt = f"""
|
||||
Based on the accessibility report you just generated for {url},
|
||||
create Playwright accessibility tests in {language}.
|
||||
|
||||
Include tests for: lang attribute, title, heading hierarchy, alt text,
|
||||
landmarks, skip navigation, focus indicators, and touch targets.
|
||||
Use Playwright's accessibility testing features with helpful comments.
|
||||
Output the complete test file.
|
||||
"""
|
||||
|
||||
print("\nGenerating accessibility tests...\n")
|
||||
await session.send(MessageOptions(prompt=test_generation_prompt))
|
||||
await done.wait()
|
||||
|
||||
print("\n\n=== Tests Generated ===")
|
||||
|
||||
await session.destroy()
|
||||
await client.stop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Playwright MCP server**: Configures a local MCP server running `@playwright/mcp` to provide browser automation tools
|
||||
2. **Streaming output**: Uses `streaming=True` and `ASSISTANT_MESSAGE_DELTA` events for real-time token-by-token output
|
||||
3. **Accessibility snapshot**: Playwright's `browser_snapshot` tool captures the full accessibility tree of the page
|
||||
4. **Structured report**: The prompt engineers a consistent WCAG-aligned report format with emoji severity indicators
|
||||
5. **Test generation**: Optionally detects the project language and generates Playwright accessibility tests
|
||||
|
||||
## Key concepts
|
||||
|
||||
### MCP server configuration
|
||||
|
||||
The recipe configures a local MCP server that runs alongside the session:
|
||||
|
||||
```python
|
||||
session = await client.create_session(SessionConfig(
|
||||
mcp_servers={
|
||||
"playwright": {
|
||||
"type": "local",
|
||||
"command": "npx",
|
||||
"args": ["@playwright/mcp@latest"],
|
||||
"tools": ["*"],
|
||||
}
|
||||
},
|
||||
))
|
||||
```
|
||||
|
||||
This gives the model access to Playwright browser tools like `browser_navigate`, `browser_snapshot`, and `browser_click`.
|
||||
|
||||
### Streaming with events
|
||||
|
||||
Unlike `send_and_wait`, this recipe uses streaming for real-time output:
|
||||
|
||||
```python
|
||||
def handle_event(event: SessionEvent):
|
||||
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
|
||||
print(event.data.delta_content or "", end="", flush=True)
|
||||
elif event.type.value == "session.idle":
|
||||
done.set()
|
||||
|
||||
session.on(handle_event)
|
||||
```
|
||||
|
||||
## Sample interaction
|
||||
|
||||
```
|
||||
=== Accessibility Report Generator ===
|
||||
|
||||
Enter URL to analyze: github.com
|
||||
|
||||
Analyzing: https://github.com
|
||||
Please wait...
|
||||
|
||||
📊 Accessibility Report: GitHub (github.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en" properly set |
|
||||
| Page Title | ✅ Pass | "GitHub" is recognizable |
|
||||
| Heading Hierarchy | ✅ Pass | Proper H1/H2 structure |
|
||||
| Images | ✅ Pass | All images have alt text |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🟡 Medium | Some links lack descriptive text | 2.4.4 | Add aria-label to icon-only links |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: 47
|
||||
- Total Headings: 8 (1× H1, proper hierarchy)
|
||||
- Focusable Elements: 52
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ✅, footer ✅
|
||||
|
||||
=== Report Complete ===
|
||||
|
||||
Would you like to generate Playwright accessibility tests? (y/n): y
|
||||
|
||||
Detecting project language...
|
||||
TypeScript detected (package.json found)
|
||||
|
||||
Confirm language for tests (or enter a different one):
|
||||
|
||||
Generating accessibility tests...
|
||||
[Generated test file output...]
|
||||
|
||||
=== Tests Generated ===
|
||||
```
|
||||
171
cookbook/copilot-sdk/python/recipe/accessibility_report.py
Normal file
171
cookbook/copilot-sdk/python/recipe/accessibility_report.py
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
from copilot import (
|
||||
CopilotClient, SessionConfig, MessageOptions,
|
||||
SessionEvent, SessionEventType,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# Main Application
|
||||
# ============================================================================
|
||||
|
||||
async def main():
|
||||
print("=== Accessibility Report Generator ===\n")
|
||||
|
||||
url = input("Enter URL to analyze: ").strip()
|
||||
|
||||
if not url:
|
||||
print("No URL provided. Exiting.")
|
||||
return
|
||||
|
||||
# Ensure URL has a scheme
|
||||
if not url.startswith("http://") and not url.startswith("https://"):
|
||||
url = "https://" + url
|
||||
|
||||
print(f"\nAnalyzing: {url}")
|
||||
print("Please wait...\n")
|
||||
|
||||
# Create Copilot client with Playwright MCP server
|
||||
client = CopilotClient()
|
||||
await client.start()
|
||||
|
||||
session = await client.create_session(SessionConfig(
|
||||
model="claude-opus-4.6",
|
||||
streaming=True,
|
||||
mcp_servers={
|
||||
"playwright": {
|
||||
"type": "local",
|
||||
"command": "npx",
|
||||
"args": ["@playwright/mcp@latest"],
|
||||
"tools": ["*"],
|
||||
}
|
||||
},
|
||||
))
|
||||
|
||||
done = asyncio.Event()
|
||||
|
||||
# Set up streaming event handling
|
||||
def handle_event(event: SessionEvent):
|
||||
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
|
||||
print(event.data.delta_content or "", end="", flush=True)
|
||||
elif event.type.value == "session.idle":
|
||||
done.set()
|
||||
elif event.type.value == "session.error":
|
||||
print(f"\nError: {event.data.message}")
|
||||
done.set()
|
||||
|
||||
session.on(handle_event)
|
||||
|
||||
prompt = f"""
|
||||
Use the Playwright MCP server to analyze the accessibility of this webpage: {url}
|
||||
|
||||
Please:
|
||||
1. Navigate to the URL using playwright-browser_navigate
|
||||
2. Take an accessibility snapshot using playwright-browser_snapshot
|
||||
3. Analyze the snapshot and provide a detailed accessibility report
|
||||
|
||||
Format the report EXACTLY like this structure with emoji indicators:
|
||||
|
||||
📊 Accessibility Report: [Page Title] (domain.com)
|
||||
|
||||
✅ What's Working Well
|
||||
| Category | Status | Details |
|
||||
|----------|--------|---------|
|
||||
| Language | ✅ Pass | lang="en-US" properly set |
|
||||
| Page Title | ✅ Pass | "[Title]" is descriptive |
|
||||
| Heading Hierarchy | ✅ Pass | Single H1, proper H2/H3 structure |
|
||||
| Images | ✅ Pass | All X images have alt text |
|
||||
| Viewport | ✅ Pass | Allows pinch-zoom (no user-scalable=no) |
|
||||
| Links | ✅ Pass | No ambiguous "click here" links |
|
||||
| Reduced Motion | ✅ Pass | Supports prefers-reduced-motion |
|
||||
| Autoplay Media | ✅ Pass | No autoplay audio/video |
|
||||
|
||||
⚠️ Issues Found
|
||||
| Severity | Issue | WCAG Criterion | Recommendation |
|
||||
|----------|-------|----------------|----------------|
|
||||
| 🔴 High | No <main> landmark | 1.3.1, 2.4.1 | Wrap main content in <main> element |
|
||||
| 🔴 High | No skip navigation link | 2.4.1 | Add "Skip to content" link at top |
|
||||
| 🟡 Medium | Focus outlines disabled | 2.4.7 | Default outline is none - ensure visible :focus styles exist |
|
||||
| 🟡 Medium | Small touch targets | 2.5.8 | Navigation links are 37px tall (below 44px minimum) |
|
||||
|
||||
📋 Stats Summary
|
||||
- Total Links: X
|
||||
- Total Headings: X (1× H1, proper hierarchy)
|
||||
- Focusable Elements: X
|
||||
- Landmarks Found: banner ✅, navigation ✅, main ❌, footer ✅
|
||||
|
||||
⚙️ Priority Recommendations
|
||||
- Add <main> landmark - Wrap page content in <main role="main"> for screen reader navigation
|
||||
- Add skip link - Hidden link at start: <a href="#main-content" class="skip-link">Skip to content</a>
|
||||
- Increase touch targets - Add padding to nav links and tags to meet 44×44px minimum
|
||||
- Verify focus styles - Test keyboard navigation; add visible :focus or :focus-visible outlines
|
||||
|
||||
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
|
||||
Include actual findings from the page analysis - don't just copy the example.
|
||||
"""
|
||||
|
||||
await session.send(MessageOptions(prompt=prompt))
|
||||
await done.wait()
|
||||
|
||||
print("\n\n=== Report Complete ===\n")
|
||||
|
||||
# Prompt user for test generation
|
||||
generate_tests = input("Would you like to generate Playwright accessibility tests? (y/n): ").strip().lower()
|
||||
|
||||
if generate_tests in ("y", "yes"):
|
||||
done.clear()
|
||||
|
||||
detect_language_prompt = """
|
||||
Analyze the current working directory to detect the primary programming language used in this project.
|
||||
Look for project files like package.json, *.csproj, pom.xml, requirements.txt, go.mod, etc.
|
||||
|
||||
Respond with ONLY the detected language name (e.g., "TypeScript", "JavaScript", "C#", "Python", "Java")
|
||||
and a brief explanation of why you detected it.
|
||||
If no project is detected, suggest "TypeScript" as the default for Playwright tests.
|
||||
"""
|
||||
|
||||
print("\nDetecting project language...\n")
|
||||
await session.send(MessageOptions(prompt=detect_language_prompt))
|
||||
await done.wait()
|
||||
|
||||
language = input("\n\nConfirm language for tests (or enter a different one): ").strip()
|
||||
if not language:
|
||||
language = "TypeScript"
|
||||
|
||||
done.clear()
|
||||
|
||||
test_generation_prompt = f"""
|
||||
Based on the accessibility report you just generated for {url}, create Playwright accessibility tests in {language}.
|
||||
|
||||
The tests should:
|
||||
1. Verify all the accessibility checks from the report
|
||||
2. Test for the issues that were found (to ensure they get fixed)
|
||||
3. Include tests for:
|
||||
- Page has proper lang attribute
|
||||
- Page has descriptive title
|
||||
- Heading hierarchy is correct (single H1, proper nesting)
|
||||
- All images have alt text
|
||||
- No autoplay media
|
||||
- Landmark regions exist (banner, nav, main, footer)
|
||||
- Skip navigation link exists and works
|
||||
- Focus indicators are visible
|
||||
- Touch targets meet minimum size requirements
|
||||
4. Use Playwright's accessibility testing features
|
||||
5. Include helpful comments explaining each test
|
||||
|
||||
Output the complete test file that can be saved and run.
|
||||
Use the Playwright MCP server tools if you need to verify any page details.
|
||||
"""
|
||||
|
||||
print("\nGenerating accessibility tests...\n")
|
||||
await session.send(MessageOptions(prompt=test_generation_prompt))
|
||||
await done.wait()
|
||||
|
||||
print("\n\n=== Tests Generated ===")
|
||||
|
||||
await session.destroy()
|
||||
await client.stop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user