accessiBe Help Center

accessFlow SDK - JavaScript/TypeScript

  • Updated

Use the accessFlow SDK with Playwright or Selenium for JavaScript and TypeScript applications to run automated accessibility audits as part of your test suite. This helps you catch accessibility issues early, and track results in the accessFlow Audits page.

How it works

Local development

  • Audits run on the selected pages
  • JSON reports saved to ./test-results directory
  • Reports are NOT uploaded to the accessFlow Audits page (prevents accidental uploads)

CI/CD environment

  • Audits run on the selected pages
  • Results aggregated from parallel test workers
  • Automatically uploaded to the accessFlow Audits page
  • Threshold checks applied (fail build if exceeded)

Prerequisites

You'll need:

  • NPM install token: Used to install the SDK package (double base64-encoded)
  • NPM registry URL: The private package registry location  (for remote installation - provided by accessiBe)
  • SDK API key: Used to authenticate audit requests at runtime (see Generate a token)

Important: The NPM install token is used to download/install the SDK package. The SDK API key is used at runtime when running accessibility audits. These are two separate credentials.

Requirements

  • TypeScript 4.0+
  • Playwright for JavaScript/TypeScript
  • Playwright browsers must be installed.
npx playwright install

Install the SDK

Install from a private package registry (recommended)

Step 1: Configure .npmrc

Create a .npmrc file in your project root:

@acsbe:registry=https://NPM_REGISTRY_URL/
//NPM_REGISTRY_URL/:username=_json_key_base64
//NPM_REGISTRY_URL/:_password=NPM_INSTALL_TOKEN
//NPM_REGISTRY_URL/:always-auth=true

Replace:

  • NPM_REGISTRY_URL
  • NPM_INSTALL_TOKEN

with the credentials provided by accessFlow.

Use the registry URL without https://  (e.g. us-east1-npm.pkg.dev/PROJECT/REPO). The // on the auth lines is part of the key format, not a comment.

Why Double Base64?

npm automatically base64-decodes the _password  field before sending it to the registry. Since GCP Artifact Registry expects a base64-encoded service account key as the password, the token must be encoded twice so that after npm decodes it once, the correct value is sent. 

The accessFlow team provides the token pre-encoded - paste it as-is.

Step 2: Install the SDK

npm install -D @acsbe/accessflow-sdk

In CI/CD environments, store the token as a secret (e.g. ACCESSFLOW_NPM_REGISTRY_TOKEN) and reference it in your .npmrc:

@acsbe:registry=https://NPM_REGISTRY_URL/
//NPM_REGISTRY_URL/:username=_json_key_base64
//NPM_REGISTRY_URL/:_password=${ACCESSFLOW_NPM_REGISTRY_TOKEN}
//NPM_REGISTRY_URL/:always-auth=true

npm automatically interpolates ${ENV_VAR}  references in .npmrc  files.

Install from a local package file

If you received a .tgz file:

npm install -D /path/to/acsbe-accessflow-sdk.tgz

Configure the SDK API key

First generate a token in accessFlow and then use it to set the API key.

Generate a token

  1. Go to your Profile menu in top right corner and select Token management
  2. Select Generate token.
  3. Enter a token name.
  4. Select Audit SDK token and then Generate token.
  5. Copy the token and store it securely. You won't be able to see it again.

Provide the API key

Recommended: Use an environment variable

The SDK automatically reads this value - no additional configuration or initialization is required.

# .env file (use with dotenv)
ACCESSFLOW_SDK_API_KEY=your-api-key

 

// No init needed - SDK reads from environment automatically
import { test } from '@playwright/test';
import AccessFlowSDK from '@acsbe/accessflow-sdk';

test('accessibility check', async ({ page }) => {
  const sdk = new AccessFlowSDK(page);
  // SDK automatically uses ACCESSFLOW_SDK_API_KEY from environment
});

For CI/CD: environments store the API key as a secret:

# GitHub Actions
env:
  ACCESSFLOW_SDK_API_KEY: ${{ secrets.ACCESSFLOW_SDK_API_KEY }}

Optional: Pass the API key explicitly

You can also provide the accessFlow API key programmatically by calling AccessFlowSDK.init once prior to running your test suite (for example, reading from a custom config):

import AccessFlowSDK from '@acsbe/accessflow-sdk';

// Call once before your tests run
AccessFlowSDK.init({ apiKey: process.env.ACCESSFLOW_SDK_API_KEY! });

Use in your test suite

Here are examples of how to use the SDK in your test suite.

Playwright

Make sure you have installed Playwright.

npm install -D @playwright/test

Add in your tests:

import { test, expect } from '@playwright/test';
import AccessFlowSDK from '@acsbe/accessflow-sdk';

test('homepage accessibility', async ({ page }) => {
  const sdk = new AccessFlowSDK(page);

  await page.goto('https://example.com');

  const audits = await sdk.audit();
  const report = await sdk.generateReport(audits);

  console.log('Issues found:', report.numberOfIssuesFound);
  // { extreme: 0, high: 5, medium: 8, low: 12 }

  // Assert no critical issues
  expect(report.numberOfIssuesFound.extreme).toBe(0);
  expect(report.numberOfIssuesFound.high).toBeLessThanOrEqual(5);
});

Selenium

Install the Selenium package alongside the SDK:

npm install -D selenium-webdriver
npm install -D @types/selenium-webdriver  # TypeScript users

Add in your tests:

import { Builder } from 'selenium-webdriver';
import { AccessFlowSDK, SeleniumDriver } from '@acsbe/accessflow-sdk';

const driver = await new Builder().forBrowser('chrome').build();
await driver.get('https://example.com');

const sdk = new AccessFlowSDK(new SeleniumDriver(driver));
const audits = await sdk.audit();
const report = await sdk.generateReport(audits);

console.log('Issues found:', report.numberOfIssuesFound);

await driver.quit();

Integrate with Playwright

How it works:

  1. Each call to sdk.audit() automatically records results for aggregation
  2. After all tests complete, globalTeardown runs once to:
    • Aggregate all recorded audits into a single report
    • Upload the report to AccessFlow (in CI environments with ACCESSFLOW_SDK_API_KEY set)
    • Validate against configured thresholds (if any)

No manual recording needed — just call audit() in your tests and configure globalTeardown.

To generate and upload audit summaries automatically after all the tests have run, add the SDK's global teardown to your Playwright configuration. 

Playwright test

import { test, expect } from '@playwright/test';
import AccessFlowSDK from '@acsbe/accessflow-sdk';

// SDK automatically reads ACCESSFLOW_SDK_API_KEY from environment

test('homepage accessibility', async ({ page }) => {
  const sdk = new AccessFlowSDK(page);

  await page.goto('https://example.com');

  const audits = await sdk.audit();
  const report = await sdk.generateReport(audits);

  // Assert no critical issues
  expect(report.numberOfIssuesFound.extreme).toBe(0);
  expect(report.numberOfIssuesFound.high).toBeLessThanOrEqual(5);
});

Global setup/teardown

Add the following in playwright.config.ts:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  globalTeardown: require.resolve('@acsbe/accessflow-sdk/dist/src/playwright/global-teardown'),
  // ... other config
});

Integrate with Selenium

How it works:

  1. Each call to sdk.audit() automatically records results for aggregation
  2. After all tests complete, globalTeardown runs once to:
    • Aggregate all recorded audits into a single report
    • Upload the report to AccessFlow (in CI environments with ACCESSFLOW_SDK_API_KEY set)
    • Validate against configured thresholds (if any)

Selenium WebDriver

Use SeleniumDriver to wrap your WebDriver instance:

import { Builder, WebDriver } from 'selenium-webdriver';
import { AccessFlowSDK, SeleniumDriver } from '@acsbe/accessflow-sdk';

// Set ACCESSFLOW_SDK_API_KEY in your environment

async function runAccessibilityTests() {
  const driver: WebDriver = await new Builder().forBrowser('chrome').build();

  try {
    await driver.get('https://example.com');

    const sdk = new AccessFlowSDK(new SeleniumDriver(driver));
    const audits = await sdk.audit();
    const report = await sdk.generateReport(audits);

    console.log('Issues found:', report.numberOfIssuesFound);
  } finally {
    await driver.quit();
  }
}

Teardown and report upload for Selenium

globalTeardown must be invoked manually after all tests complete. Import and call it from your test runner's after-all hook. This aggregates all recorded audits, uploads the report to accessFlow (when ACCESSFLOW_SDK_API_KEY  is set in CI environments), and validates thresholds.

import { globalTeardown } from '@acsbe/accessflow-sdk';

// In your test framework's after-all hook, e.g.:
// - Jest: afterAll
// - Mocha: after (root-level)
// - node:test: after
afterAll(async () => {
  await globalTeardown(null as any);
});

Chrome/ChromeDriver setup

Selenium requires Chrome and a matching ChromeDriver. In CI environments, install them before running tests:

# GitHub Actions example
- name: Install Chrome
  uses: browser-actions/setup-chrome@v1

- name: Install ChromeDriver
  uses: nanasess/setup-chromedriver@v2

Or manually:

# Ubuntu
sudo apt-get install -y google-chrome-stable
CHROME_VERSION=$(google-chrome --version | grep -oP '\d+\.\d+\.\d+\.\d+')
wget "https://storage.googleapis.com/chrome-for-testing-public/${CHROME_VERSION}/linux64/chromedriver-linux64.zip"
unzip chromedriver-linux64.zip && sudo mv chromedriver-linux64/chromedriver /usr/local/bin/

Configure the SDK (optional)

Create a configuration file

Create sdk.config.ts  in your project root:

import { SDKConfig } from '@acsbe/accessflow-sdk';

const config: SDKConfig = {
  apiKey: process.env.ACCESSFLOW_SDK_API_KEY,
  uploadEnabled: true,
  outputDir: './accessibility-reports',
  wcagLevel: 'AA', // 'A', 'AA', or 'AAA'
};

export default config;

Configure thresholds

Create an accessflow.config.json file in your project root. Use it to define thresholds for accessibility issues that will fail your test suite if exceeded. The configuration is automatically loaded during the global teardown phase. 

If no config file is provided, thresholds are not enforced (tests won’t fail because of counts). Reports are still generated.

Example 

{
  "issuesFoundThreshold": {
    "extreme": 0,
    "high": 5,
    "medium": 10,
    "low": 20
  },
  "localCheck": false
}

Options

Parameter Type Description
issuesFoundThreshold object

(Optional) Defines the maximum allowed number of accessibility issues per severity. Non-numeric values are ignored.

  • extreme: (number) Max extreme issues allowed.
  • high: (number) Max high issues allowed.
  • medium: (number) Max medium issues allowed.
  • low: (number) Max low issues allowed.
localCheck boolean

(Optional) Whether threshold checking is enforced during test execution.

  • true: Violations will fail the tests with a detailed error message. Default. Provides immediate feedback during development and CI/CD pipelines. 
  • false: Violations are recorded in the report, but don't fail tests. Useful for monitoring and gradual improvement without breaking builds.

Final report and localCheck

Final report

When all tests have run, the global teardown (or Selenium globalTeardown() call) aggregates every recorded audit into a single report, uploads it to the accessFlow Audit page in CI, and validates it  against issuesFoundThreshold. No extra code is required. The report JSON includes:

  • pages - object keyed by URL; each value has numberOfIssuesFound (severity counts) and ruleViolations (rule id → description, severity, selectors, selectorData with HTML/suggestionLabel/suggestionType, WCAG level/link).
  • totalNumberOfIssuesFound - aggregated severity counts across all pages.

Example shape (abbreviated):

{
  "pages": {
    "https://example.com/1": {
      "numberOfIssuesFound": { "extreme": 0, "high": 1, "medium": 1, "low": 0 },
      "ruleViolations": {
        "IMG_MISSING_ALT": {
          "description": "Images must have alternate text",
          "name": "Images must have alternate text",
          "severity": "high",
          "selectors": ["img.hero"],
          "selectorData": [{ "selector": "img.hero", "HTML": "<img src=\"/banner.png\">", "suggestionLabel": "Add alt text", "suggestionType": "addAttribute" }],
          "WCAGLevel": "A",
          "WCAGLink": "https://www.w3.org/TR/WCAG21/#text-alternatives"
        },
        "HEADING_ORDER": {
          "description": "Headings must be in order",
          "name": "Heading order",
          "severity": "medium",
          "selectors": ["h3:first-of-type"],
          "selectorData": [{ "selector": "h3:first-of-type", "HTML": "<h3>Section</h3>", "suggestionLabel": "Use h2 before h3", "suggestionType": "changeTag" }],
          "WCAGLevel": "A",
          "WCAGLink": "https://www.w3.org/TR/WCAG21/#info-and-relationships"
        }
      }
    },
    "https://example.com/2": {
      "numberOfIssuesFound": { "extreme": 0, "high": 1, "medium": 1, "low": 0 },
      "ruleViolations": {
        "LINK_EMPTY": {
          "description": "Links must have discernible text",
          "name": "Links must have discernible text",
          "severity": "high",
          "selectors": ["a.nav-link", "a[href='#']"],
          "selectorData": [
            { "selector": "a.nav-link", "HTML": "<a class=\"nav-link\" href=\"/about\"></a>", "suggestionLabel": "Add link text", "suggestionType": "addContent" },
            { "selector": "a[href='#']", "HTML": "<a href=\"#\"> </a>", "suggestionLabel": "Add link text", "suggestionType": "addContent" }
          ],
          "WCAGLevel": "A",
          "WCAGLink": "https://www.w3.org/TR/WCAG21/#link-purpose-in-context"
        },
        "CONTRAST_MINIMUM": {
          "description": "Text must meet minimum contrast ratio",
          "name": "Contrast (Minimum)",
          "severity": "medium",
          "selectors": [".muted"],
          "selectorData": [{ "selector": ".muted", "HTML": "<span class=\"muted\">Disclaimer</span>", "suggestionLabel": "Increase contrast", "suggestionType": "changeStyle" }],
          "WCAGLevel": "AA",
          "WCAGLink": "https://www.w3.org/TR/WCAG21/#contrast-minimum"
        }
      }
    }
  },
  "totalNumberOfIssuesFound": { "extreme": 0, "high": 2, "medium": 2, "low": 0 }
}

localCheck

By default, thresholds are only enforced in CI environments. To fail local runs when limits are exceeded, set "localCheck": true in accessflow.config.json:

{
  "issuesFoundThreshold": { "extreme": 0, "high": 5, "medium": 10, "low": 20 },
  "localCheck": true
}

With localCheck: true, running npm test (or your test command) locally will exit with an error and print issue counts if the aggregated report exceeds the configured limits.

Configure your CI/CD for accessFlow

In your CI pipelines, configure these secrets and variables. This will ensure your test results are displayed in the accessFlow Audits page.

Secrets

  • ACCESSFLOW_NPM_REGISTRY_TOKEN: To install the SDK
  • ACCESSFLOW_SDK_API_KEY: To authenticate audit requests

Variables

Variable Purpose Example
GCP_SDK_NPM_REGISTRY_URL NPM registry URL (without https://) us-east1-npm.pkg.dev/PROJECT/stag-accessflow-npm

Important: Do not include https://  in the registry URL variable - it's added automatically by the .npmrc  configuration.

Your .npmrc should reference these environment variables.

GitHub Actions example

```yaml
name: Accessibility Tests

on: [push]

jobs:
  test:
    runs-on: ubuntu-22.04  # Use 22.04 for Playwright compatibility
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npm install
        env:
          ACCESSFLOW_NPM_REGISTRY_TOKEN: ${{ secrets.ACCESSFLOW_NPM_REGISTRY_TOKEN }}
      - run: npx playwright install chromium
      - run: npm test
        env:
          ACCESSFLOW_SDK_API_KEY: ${{ secrets.ACCESSFLOW_SDK_API_KEY }}
```

CircleCI example

jobs:
  test:
    docker:
      - image: mcr.microsoft.com/playwright:v1.40.0-focal
    steps:
      - checkout
      - run: npm install
      - run: npm test
    environment:
      ACCESSFLOW_NPM_REGISTRY_TOKEN: ${ACCESSFLOW_NPM_REGISTRY_TOKEN}
      ACCESSFLOW_SDK_API_KEY: ${ACCESSFLOW_SDK_API_KEY}

Jenkins example

 

API Reference

Constructor

AccessFlowSDK.init(options)

Initializes the SDK configuration (optional).

AccessFlowSDK.init({ apiKey: 'your-api-key' });

new AccessFlowSDK(page, testInfo?, options?)

Creates a new SDK instance for a page. If testInfo is provided, HTML or JSON reports are attached directly to the test results.

new AccessFlowSDK(page, testInfo?, options?)
Parameter Description
page Playwright Page object
testInfo Playwright testInfo
options Threshold configuration 
const sdk = new AccessFlowSDK(page);

Run an audit

Runs an accessibility audit on the current page.

await sdk.audit()

const audits = await sdk.audit();

Returns

Returns audit results including:

  • Severity levels
  • Rule violations
  • WCAG references
  • Element selectors

Generate a report

Takes the results returned by audit() and produces a structured report. counting issues by severity. Can be generated per test.  

When exportType is set to "html" or "json", a human readable report is attached to the test results. (when testInfo is provided) or written to the test-results folder.

await sdk.generateReport(audits)

const report = await sdk.generateReport(audits);

console.log(report.numberOfIssuesFound);
// { extreme: 0, high: 5, medium: 8, low: 12 }

Returns

Parameter Description
numberOfIssuesFound The number of extreme, high, medium and low issues found.
ruleViolations A list of rules violated including severity, WCAG level and which selector was involved.
Additional metadata  

Environment variables

Variable Required Purpose
ACCESSFLOW_SDK_API_KEY Yes Your accessFlow SDK API key (for runtime authentication)
ACCESSFLOW_NPM_REGISTRY_TOKEN CI/CD only Double base64-encoded registry token (referenced in .npmrc)
ACCESSFLOW_OUTPUT_DIR Optional Custom report directory

TypeScript support

The SDK is written in TypeScript and includes full type definitions:

import AccessFlowSDK, {
  IAudits,
  AuditReport,
  SDKConfig,
  SeleniumDriver,
  PlaywrightDriver,
  IBrowserDriver,
} from '@acsbe/accessflow-sdk';

const config: SDKConfig = {
  apiKey: 'your-key',
  wcagLevel: 'AA',
};

AccessFlowSDK.init(config);

// Playwright — types are automatically inferred
const audits: IAudits = await sdk.audit();
const report: AuditReport = await sdk.generateReport(audits);

// Selenium — explicit driver type
const driver: IBrowserDriver = new SeleniumDriver(webDriver);
const seleniumSdk = new AccessFlowSDK(driver);

Troubleshooting

API key not found

// Check if environment variable is set
console.log('API Key:', process.env.ACCESSFLOW_SDK_API_KEY ? 'Set' : 'Missing');

// Or initialize manually
AccessFlowSDK.init({ apiKey: 'your-api-key' });

Reports not uploading

  • Ensure the SDK is running as part of your CI environment.
  • Confirm the API key is valid
// Check if CI is detected
console.log('CI detected:', process.env.CI);

// Disable uploads for testing
process.env.ACCESSFLOW_UPLOAD_ENABLED = 'false';

Playwright browser issues

Make sure the Playwright browsers are properly initialized.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();

const sdk = new AccessFlowSDK(page);
// Now you can use the SDK

Selenium WebDriver issues

Ensure Chrome and ChromeDriver versions match:

google-chrome --version
chromedriver --version
# Both should report the same major version

If Selenium can't find the driver binary, pass the path explicitly:

import { Builder, Capabilities } from 'selenium-webdriver';
import chrome from 'selenium-webdriver/chrome';

const options = new chrome.Options();
options.addArguments('--headless', '--no-sandbox', '--disable-dev-shm-usage');

const driver = await new Builder()
  .forBrowser('chrome')
  .setChromeOptions(options)
  .build();

Was this article helpful?

0 out of 0 found this helpful