Accessibility Automation

@phenomnominal 2019

Is HArd, yes?
@phenomnominal 2019

Static ANalysis
@phenomnominal 2019
Lighthouse

@phenomnominal 2019

@phenomnominal 2019

@phenomnominal 2019
@phenomnominal 2019

Colour
@phenomnominal 2019

@phenomnominal 2019

@phenomnominal 2019

@phenomnominal 2019
Content

@phenomnominal 2019


AXE
@phenomnominal 2019

@phenomnominal 2019
@phenomnominal 2019
AXE + PUPPEteer

@phenomnominal 2019

@phenomnominal 2019
const { AxePuppeteer } = require("axe-puppeteer");
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.setBypassCSP(true);
await page.goto("https://open.spotify.com");
const results = await new AxePuppeteer(page).analyze();
console.log(results);
await page.close();
await browser.close();
})();
@phenomnominal 2019
AXE + PUPPEteer + JEST

@phenomnominal 2019

@phenomnominal 2019
const { AxePuppeteer } = require("axe-puppeteer");
const puppeteer = require("puppeteer");
test(`accessibility hasn't got worst`, async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setBypassCSP(true);
await page.goto("https://open.spotify.com");
const results = await new AxePuppeteer(page).analyze();
const violations = results.violations.map(violation => {
delete violation.nodes;
return violation;
});
expect(violations).toMatchSnapshot();
await page.close();
await browser.close();
});
@phenomnominal 2019
PUPPEteer + JEST + Keyboard

@phenomnominal 2019
const puppeteer = require("puppeteer");
test(`can search with keyboard`, async () => {
jest.setTimeout(30000);
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.setBypassCSP(true);
await page.goto("https://open.spotify.com");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Enter");
await page.keyboard.type("Propagandhi", { delay: 200 });
await page.keyboard.press("Enter");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Enter");
await page.close();
await browser.close();
});
@phenomnominal 2019
PUPPEteer + JEST + VOICEOVER + SPEECH-To-TEXT
⛏
@phenomnominal 2019
require("dotenv").config();
const util = require("util");
const path = require("path");
const exec = util.promisify(require("child_process").exec);
const recorder = require("node-record-lpcm16");
const fs = require("fs");
const speech = require("@google-cloud/speech");
const puppeteer = require("puppeteer");
async function voiceover() {
const outputPath = path.join(__dirname, "./output.wav");
const file = fs.createWriteStream(outputPath, {
encoding: "binary"
});
const recording = recorder.record({
sampleRate: 44100
});
recording.stream().pipe(file);
const toggleVoiceoverScriptPath = path.resolve(
__dirname,
"./toggle-voiceover.scpt"
);
try {
await exec(`osascript ${toggleVoiceoverScriptPath}`);
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.setBypassCSP(true);
await page.goto("https://open.spotify.com/show/54XPfcl9oJdktfD1lT5V2N");
await page.keyboard.press("Tab", { delay: 0 });
await page.keyboard.press("Tab", { delay: 15000 });
await page.keyboard.press("Tab", { delay: 2000 });
await page.keyboard.press("Tab", { delay: 2000 });
await page.keyboard.press("Tab", { delay: 2000 });
await page.keyboard.press("Tab", { delay: 2000 });
await page.close();
await browser.close();
await exec(`osascript ${toggleVoiceoverScriptPath}`);
recording.stop();
const speechClient = new speech.SpeechClient();
const file = fs.readFileSync(outputPath);
const audioBytes = file.toString("base64");
const audio = {
content: audioBytes
};
// The audio file's encoding, sample rate in hertz, and BCP-47 language code
const config = {
sampleRateHertz: 44100,
languageCode: "en-US"
};
const request = {
audio,
config
};
// Detects speech in the audio file
speechClient
.recognize(request)
.then(data => {
console.log(data[0].results[0].alternatives[0].transcript);
})
.catch(err => {
console.error("ERROR:", err);
});
} catch (e) {
console.log(e);
}
}
voiceover();
@phenomnominal 2019
What ELSE?
@phenomnominal 2019

Captions?
Animations?
@phenomnominal 2019

Questions?
@phenomnominal 2019
Fin.




Accessibility Automation
By Craig Spence
Accessibility Automation
- 3,223