id: accessibility-test
namespace: company.team
description: |
Automatically audit a website for accessibility issues using Playwright,
axe-core, and Google Gemini. The workflow detects accessibility violations,
uses AI to explain and prioritize the findings, and generates an audit report.
labels:
- key: category
value: accessibility
- key: technology
value: playwright
- key: technology
value: axe-core
- key: technology
value: ai
inputs:
- id: target_url
type: STRING
defaults: https://example.com
description: Website URL to audit
triggers:
- id: daily_audit
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 9 * * *"
tasks:
- id: scan_website
type: io.kestra.plugin.scripts.node.Script
containerImage: node:20
beforeCommands:
- npm install playwright axe-core
- npx playwright install --with-deps chromium
outputFiles:
- accessibility.json
script: |
const { chromium } = require("playwright");
const fs = require("fs");
(async () => {
const browser = await chromium.launch({
headless: true
});
const page = await browser.newPage();
await page.goto("{{ inputs.target_url }}", {
waitUntil: "networkidle"
});
await page.addScriptTag({
path: require.resolve("axe-core/axe.min.js")
});
const results = await page.evaluate(async () => {
return await axe.run();
});
const report = {
url: "{{ inputs.target_url }}",
totalViolations: results.violations.length,
violations: results.violations.map(v => ({
id: v.id,
impact: v.impact,
description: v.description,
help: v.help,
helpUrl: v.helpUrl,
affectedElements: v.nodes.map(node => ({
html: node.html,
target: node.target,
failureSummary: node.failureSummary
}))
}))
};
fs.writeFileSync(
"accessibility.json",
JSON.stringify(report, null, 2)
);
console.log("=== ACCESSIBILITY SCAN COMPLETE ===");
console.log("Violations found:", results.violations.length);
console.log("Created accessibility.json");
await browser.close();
})();
- id: ai_analysis
type: io.kestra.plugin.ai.agent.AIAgent
provider:
type: io.kestra.plugin.ai.provider.GoogleGemini
modelName: gemini-3.5-flash-lite
apiKey: "{{ secret('GEMINI_API_KEY') }}"
systemMessage: |
You are an accessibility expert analyzing axe-core results.
Only analyze the violations provided in the input.
Do not invent additional violations.
Classify each violation as:
- CRITICAL: serious accessibility problem that significantly blocks access
- WARNING: accessibility problem that should be fixed but is not necessarily blocking
- PASS: no accessibility problem
Determine the overall severity as:
- CRITICAL if at least one violation is CRITICAL
- WARNING if there are no CRITICAL violations but at least one WARNING
- PASS if there are no violations
Return ONLY valid JSON.
Do not use Markdown.
Do not wrap the JSON in ```json code fences.
Use exactly this structure:
{
"overallSeverity": "CRITICAL | WARNING | PASS",
"criticalCount": 0,
"warningCount": 0,
"pass": false,
"summary": "Short overall summary",
"violations": [
{
"id": "axe violation id",
"severity": "CRITICAL | WARNING | PASS",
"problem": "What is wrong",
"whyItMatters": "Why this matters to users",
"recommendedFix": "Practical fix"
}
]
}
prompt: |
Analyze this axe-core accessibility scan:
{{ read(outputs.scan_website.outputFiles['accessibility.json']) }}
- id: severity_switch
type: io.kestra.plugin.core.flow.Switch
value: "{{ fromJson(outputs.ai_analysis.textOutput).overallSeverity }}"
cases:
CRITICAL:
- id: critical_report
type: io.kestra.plugin.core.log.Log
message: "CRITICAL accessibility issues detected."
WARNING:
- id: warning_report
type: io.kestra.plugin.core.log.Log
message: "WARNING accessibility issues detected."
PASS:
- id: pass_report
type: io.kestra.plugin.core.log.Log
message: "No accessibility issues detected."
defaults:
- id: unknown_severity
type: io.kestra.plugin.core.log.Log
message: "Could not determine accessibility severity."
- id: create_report
type: io.kestra.plugin.scripts.node.Script
containerImage: node:20
outputFiles:
- accessibility-report.txt
script: |
const fs = require("fs");
const report = `
ACCESSIBILITY AUDIT REPORT
==========================
Website:
{{ inputs.target_url }}
AI ANALYSIS:
{{ outputs.ai_analysis.textOutput }}
`;
fs.writeFileSync(
"accessibility-report.txt",
report
);
console.log("=== ACCESSIBILITY REPORT CREATED ===");
console.log(report);