Search code examples
htmlnode.jsaccessibilitypa11y

How to check the accessibility with Node.js of the raw HTML code, not URI?


I need to check the accessibility of HTML code, herewith:

  1. I need the Node.js API (class of function), not CLI.
  2. I want to pass the HTML string as parameter, not the URI of file path.

Is should be something like:

import AccessibilityInspector from "package-which_i_need";

AccessibilityInspector.inspect({
  rawHTML_Code: `<!doctypehtml><html lang=en><meta charset=utf-8><title>title</title><link href=style.css rel=stylesheet><script src=script.js></script>`,
  standard: "WCAG2AAA"
}).
   then((issues: Array<AccessibilityInspector.Issue>): void => {
     // Log the issues
   }).
   catch((error: unknown) => {
     console.error(error);
   })

Below packages does not satisfy to above conditions:

  • The pa11y accepts only URI as the first parameter, but not the HTML code.
  • According the documentation, the access-sniff accepts only URIs too. Although the raw HTML could be also passed, "Pattern is too long" unclear error could occur. Also, the access-sniff has many vulnerabilities and not being maintained.

Other options?


Solution

  • pa11y has two modes of operation: get contents from a web page by following the URL, or get contents from the provided browser and page instances directly, without making an HTTP request. The behavior is controlled by ignoreUrl parameter, which is false by default.

    Get contents directly from browser and page

    With the help of puppeteer, you can create browser and page instances and provide them to pa11y. You would need to set ignoreUrl to true:

    import puppeteer from 'puppeteer'
    import pa11y from 'pa11y'
    
    // Here comes your raw HTML code
    const htmlCode = '<html><body>Hello world!</body></html>'
    
    async function main() {
      const browser = await puppeteer.launch()
      const page = await browser.newPage()
    
      await page.setContent(htmlCode, {
        waitUntil: 'domcontentloaded',
      })
    
      const url = page.url()
      const a11y = await pa11y(url, {
        ignoreUrl: true,
        browser,
        page,
      })
    
      console.log(a11y)
    
      await browser.close()
    }
    
    main()
    

    Get contents from a local server-hosted web page

    Alternatively, you can spin up a local server that would respond to any request with the HTML code, effectively making your own temporary website with the said HTML code.

    This can totally be implemented using the built-in http module, although it is more cumbersome that way. The below implementation uses express:

    import { type Server } from 'http'
    import express = require('express')
    import pa11y = require('pa11y')
    
    // Here comes your raw HTML code
    const htmlCode = '<html><body>Hello world!</body></html>'
    
    function getPort(server: Server) {
      const address = server.address()!
    
      if (address instanceof Object) {
        return address.port
      }
    
      return address.split(':').at(-1)!
    }
    
    const server = express()
      .use((req, res) => res.send(htmlCode))
      .listen(async () => {
        const port = getPort(server)
        const a11y = await pa11y(`http://localhost:${port}`)
    
        console.log(a11y)
    
        server.close()
      })
    

    Note that the server is attached to whatever port is available at the time, which is why we have to use getPort function (or similar).