Playwright JS SDK
The Evinced Playwright JS SDK integrates with new or existing Playwright tests to automatically detect accessibility issues. By adding a few lines of code to your Playwright project, you can begin analyzing all the web pages and DOM changes to provide a dynamic view of how your site can become more accessible. As a result of the test, a rich and comprehensive report is generated to easily track issues to resolution.
Interested in seeing this in action? Contact us to get started!
Prerequisites
- Playwright version 1.25 or higher
Get Started
Installation
The Playwright JS SDK is not publicly available. Contact us to get started!
With a local copy of the Playwright JS SDK gzipped tar package (.tgz extension), install it in your project directory using NPM or Node package manager of your choice:
1# Using NPM2npm install -D {path to Playwright JS SDK package}
Authentication
To launch Playwright JS SDK you need to have a Service ID and an API Key.
Where to find your Evinced SDK credentials
These credentials are available via the Evinced Product Hub in the
Automation for Web or Automation for Mobile product areas. Click the
Get SDK
button to see the Service Account ID and API Key at the bottom of the page.
Authenticate for Offline Testing
There are two methods to provide the token: offline mode and online mode. Online mode contacts the Evinced Licensing Server. Offline mode assumes that an Evinced employee already supplied you a JSON Web Token (JWT). If an offline token is required, please reach out to your account team or support@evinced.com.
We encourage setting credentials in environment variables and setting them in code by reference.
1# Online mode2export EVINCED_SERVICE_ID=<serviceId>3export EVINCED_API_KEY=<apiKey>45# Offline mode6# If provided a JWT by Evinced7export EVINCED_SERVICE_ID=<serviceId>8export EVINCED_AUTH_TOKEN=<token>
Example
Authenticate Playwright JS SDK in Playwright’s globalSetup
as shown below
for the typical case using CommonJS or TypeScript with CommonJS.
1// global.settings.js2const { setCredentials } = require('@evinced/js-playwright-sdk')34async function globalSetup(config) {5 try {6 await setCredentials({7 serviceId: process.env.EVINCED_SERVICE_ID,8 secret: process.env.EVINCED_API_KEY,9 })10 } catch (error) {11 throw new Error(12 'Evinced SDK authorization failure.'13 )14 }15}16module.exports = globalSetup
Set the full path to the module in the Playwright configuration:
1// playwright.config.js or playwright.config.ts2globalSetup: require.resolve('./global.setup.js')
Authenticate Using ECMAScript Modules
If package.json declares "type": "module" or the file extension is '.mjs', use ECMAScript module syntax:
1// playwright.config.mjs2// use pathToFileURL to resolve full path:3 globalSetup: pathToFileURL('./global.setup.js').pathname,
1// global.settings.mjs2import evSdk from '@evinced/js-playwright-sdk'3const { setCredentials } = evSdk45async function globalSetup(config) {6 try {7 await setCredentials({8 serviceId: process.env.EVINCED_SERVICE_ID,9 secret: process.env.EVINCED_API_KEY,10 })11 } catch (error) {12 throw new Error(13 'Evinced SDK authorization failure.'14 )15 }16}17export default globalSetup
Authenticate for Offline Testing
If Evinced has provided an JSON Web Token (JWT) for offline testing, invoke setOfflineCredentials to authenticate rather than setCredentials.
1// global.setup.js2const { setOfflineCredentials } = require('@evinced/js-playwright-sdk')34async function globalSetup(config) {5 try {6 await setOfflineCredentials({7 serviceId: process.env.EVINCED_SERVICE_ID,8 token: process.env.EVINCED_AUTH_TOKEN,9 })10 } catch (error) {11 throw new Error(12 'Evinced SDK authorization failure.'13 )14 }15}
Your First Test
SDK Initialization
To use Playwright JS SDK you need to authenticate. Please refer to Authentication section for details.
Add Evinced accessibility checks (Single Run Mode)
This is a simple example of how to add an Evinced accessibility scan to a test. Please note the inline comments that give detail on each test step.
This test scans https://demo.evinced.com, then generates an Evinced report. The test passes if the report is generated.
1// Use require in CommonJS module (default)2const { test, expect } = require('@playwright/test')3const { existsSync } = require('node:fs')4const { EvincedSDK } = require('@evinced/js-playwright-sdk')56// Use import in ECMAScript module7// (.mjs extension or type: module in package.json)8import { test, expect } from '@playwright/test'9import { existsSync } from 'node:fs'10import evExport from '@evinced/js-playwright-sdk'11const { EvincedSDK } = evExport1213// Test is the same with either module system14test.describe('Evinced Demo Page', () => {15 test('Single Run Test', async ({ page }) => {16 const evReport = './test-results/single.html'17 const evincedService = new EvincedSDK(page)18 await page.goto('https://demo.evinced.com/')19 const issues = await evincedService.evAnalyze()20 await evincedService.evSaveFile(issues, 'html', evReport)21 expect(existsSync(evReport)).toBeTruthy()22 })23})
Add Evinced accessibility checks (Continuous Mode)
This is an example of how to add a continuous Evinced accessibility scan to a test. Using the evStart()
and evStop()
methods, the Evinced engine will continually scan in the background capturing all DOM changes and page navigations as the test is executed. This will capture all accessibility issues as clicking on drop-downs or similar interactions reveals more of the page. The advantage of continuous mode is that no interaction with the actual test code is needed.
In this test, an Evinced report is generated for https://demo.evinced.com/ after some scripted interaction. The test passes if the report is generated.
1// Use require in CommonJS module (default)2const { test, expect } = require('@playwright/test')3const { existsSync } = require('node:fs')4const { EvincedSDK } = require('@evinced/js-playwright-sdk')56// Use import in ECMAScript module7import { test, expect } from '@playwright/test'8import { existsSync } from 'node:fs'9import evExport from '@evinced/js-playwright-sdk'10const { EvincedSDK } = evExport1112// Test is the same with either module system13test.describe('Evinced Demo Page', () => {14 test('Continuous Test', async ({ page }) => {15 const evReport = './test-results/continuous.html'16 const evincedService = new EvincedSDK(page)17 await evincedService.evStart()1819 await page.goto('https://demo.evinced.com/')2021 const BASE_FORM_SELECTOR =22 '#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container'23 const SELECT_HOME_DROPDOWN = `${BASE_FORM_SELECTOR} > div:nth-child(1) > div > div.dropdown.line`24 const SELECT_WHERE_DROPDOWN = `${BASE_FORM_SELECTOR} > div:nth-child(2) > div > div.dropdown.line`25 const TINY_HOME_OPTION = `${BASE_FORM_SELECTOR} > div:nth-child(1) > div > ul > li:nth-child(2)`26 const EAST_COST_OPTION = `${BASE_FORM_SELECTOR} > div:nth-child(2) > div > ul > li:nth-child(3)`2728 await page.locator(SELECT_HOME_DROPDOWN).click()29 await page.locator(TINY_HOME_OPTION).click()30 await page.locator(SELECT_WHERE_DROPDOWN).click()31 await page.locator(EAST_COST_OPTION).click()3233 const issues = await evincedService.evStop()34 await evincedService.evSaveFile(issues, 'html', evReport)35 expect(existsSync(evReport)).toBeTruthy()36 })37})
API
global configuration
Initializes the Evinced object within the project.
Example
The specific SDK initialization is not needed. Just import EvincedSDK in your test files.
1// in CommonJS module context2const { EvincedSDK } = require('@evinced/js-playwright-sdk');34// In ECMAScript module context5import sdk from '@evinced/js-playwright-sdk'6const { EvincedSDK } = sdk
Please refer to configuration to see examples of using init with options.
evAnalyze(options)
Scans the current page and returns a list of accessibility issues.
Note: This method is not supported if evStart()
is already running.
Please refer to configuration to see examples of using init with options.
Example
1const evincedService = new EvincedSDK(page);2await page.goto("https://demo.evinced.com/");3const issues = await evincedService.evAnalyze();
Return value
Promise<Issue[]>
A Report object is returned containing accessibility issues. This is the recommended method for static page analysis.
For more information regarding reports as well as the Report
object itself, please refer to our detailed Web Reports page.
evStart(options)
Continually watches for DOM mutations and page navigation and records all accessibility issues until the evStop()
method is called. This method is recommended for dynamic page flows.
Example
1const evincedService = new EvincedSDK(page);2await evincedService.evStart();3await page.goto('https://demo.evinced.com/');4const issues = await evincedService.evStop();
Return value
Promise<void>
evStop(options)
Stops the process of issue gathering started by evStart()
command.
Example
1const evincedService = new EvincedSDK(page);2await evincedService.evStart();3await page.goto('https://demo.evinced.com/');4const issues = await evincedService.evStop();
Return value
Promise<Issue[]>
Returns an object containing recorded accessibility issues from the point in time at which the evStart()
method
was instantiated. For more information regarding reports as well as the returned object itself, please refer to detailed
Web Reports page.
evSaveFile(issues, format, destination)
Saves list of the issues in a file, with the specified format and location. For example, format could be ‘json
',
'html
, 'sarif
' or 'csv
'. Please find detailed information on Web Reports page.
Example
1const evincedService = new EvincedSDK(page);2await page.goto("https://demo.evinced.com/");3const issues = await evincedService.evAnalyze();4evincedService.evSaveFile(issues, 'html', 'test-results/evinced-report.html');5evincedService.evSaveFile(issues, 'csv', 'test-results/evinced-report.csv');6evincedService.evSaveFile(issues, 'json', 'test-results/evinced-report.json');7evincedService.evSaveFile(issues, 'sarif', 'test-results/evinced-report.sarif.json');
Return value
Promise<void>
Aggregated Report
The aggregated report feature allows you to have a general aggregated report for the whole run (not only for one test or suite).
This report will contain all the issues found by the tests where evStart()
and evStop()
and evAnalyze()
commands were called.
It is still possible to use the evSaveFile()
command in any place of your code along with this Aggregated Report feature.
Example
For using the aggregated report feature you should update your config files.
First add the special entry reporterOptions to your evConfig.json
file:
1{2 "reporterOptions": {3 "reportFormat": "html", // Sets a desired format for the report. Available options are: html, sarif, and json. Mandatory.4 "fileName": "aggregatedReportForRun.html", // Specifies a name of the final report. Mandatory.5 "outputDir": "./evincedReports", // Specifies a path to the final aggregated report file. Mandatory.6 "tmpDir": "./evincedReports/tmp" // A directory for storing Evinced tmp files. 'os.tmpdir()' by default. Optional.7 }8}
Then add specify a path to the Evinced reporter in your playwright.config
file. It might be found in your node-modules directory, for example:
1'./node_modules/@evinced/js-playwright-sdk/dist/reporter/evincedReporter.js'
Important! The Evinced reporter file path might be different in your project, so please check it before specifying the path in the config file.
To do so add Evinced reporter to a list of reporters, it may look like this:
1reporter: process.env.CI ? 'html' : [['line'], ['./node_modules/@evinced/js-playwright-sdk/dist/reporter/evincedReporter.js']]
Important! Setting of the reportFormat
, fileName
, and outputDir
parameters is mandatory, the report will not be generated if some of them are missing. Setting of the tmpDir
is optional, the tmp directory of you environment will be used by default.
And that's it! Now you can run your tests and get the aggregated report in the specified directory.
Adding Aggregated Report when using the Cucumber test reporter
In some cases when using a Cucumber reporter, you may need to add the Evinced reporter to your `BeforeAll` and `AfterAll` hooks in the following way:
1// Import the Evinced reporter,2// it may look like this in your project (in case of using commonjs):3const EvincedReporter = require('@evinced/js-playwright-sdk/reporter');
Add the Evinced reporter to your BeforeAll
hook:
1BeforeAll(async function () {2 // your code here3 new EvincedReporter().onBegin();4});
Add the Evinced reporter to your AfterAll
hook:
1AfterAll(() => {2 // your code here3 new EvincedReporter().onEnd();4});
Configuration
The same configuration object can be used in global configuration
, evStart()
and evAnalyze()
methods but
with a bit different consequences. By providing some options in global configuration
method you define a global
configuration for all calls of evStart()
or evAnalyze()
methods. Please note that global configuration is not
intended to be altered from tests due to it will affect all the rest tests as well as tests running in parallel threads.
To alter configuration in specific tests you can provide config on the method's level, for instance with evStart()
it
defines local configuration for this particular session until evStop()
is called.
If provided in both levels, the command's level config will override the global one.
Engines configuration
Evinced uses two separate engines when scanning for accessibility issues, one is the aXe engine and the other is the Evinced engine. By default, Evinced disables the aXe Needs Review and Best Practices issues based on customer request and due to the fact they are mostly false positives. Please note this setting when comparing issue counts directly. See an example of how to enable Needs Review and Best practices issues in our toggles section.
Configuration object type
To define global configuration for Playwright JS SDK please use file evConfig.json
in a root directory of your project.
1export type EvInitOptions = {2 rootSelector?: string;3 enableScreenshots?: boolean;4 axeConfig?: axe.RunOptions;5 skipValidations?: SkipValidation[];6 logging?: {7 LOGGING_LEVEL: string;8 ADD_LOGGING_CONTEXT?: boolean;9 };10 toggles?: { [key: string]: boolean };11};
Root Selector
Sets a CSS selector to limit the Evinced Engine to scan only the selected element and its children. Must be a valid CSS selector. If not set, the Evinced Engine will scan the entire document.
null
by default.
Example
1const evincedService = new EvincedSDK(page);2await evincedService.evStart({3 rootSelector: '.some-selector'4});5await page.goto('https://demo.evinced.com/');6const issues = await evincedService.evStop();
AXE Configuration
Evinced leverages Axe open-source accessibility toolkit as part of its own accessibility detection engine. The Evinced engine is able to pinpoint far more issues than Axe alone.
For the full Axe config options, see Axe Core API.
Example
1const evincedService = new EvincedSDK(page);2await page.goto('https://demo.evinced.com/');3const issues = await evincedService.evAnalyze({4 "axeConfig": {5 "rules": {6 "link-name": { "enabled": false }7 }8 },9});
Errors Strict Mode
When set to true
it switches SDK in mode when Evinced SDK errors are thrown as runtime errors and stops current test
execution. Otherwise errors are printed into console except critical ones.
false
by default.
Example
The feature **is not yet implemented** in this SDK. If you'd like to increase the priority of it, please contact support@evinced.com.
Engine Logging
Switches logging on for engine. Log messages from the engine will be printed to console.
Levels are: debug
, info
, warn
, error
null
by default.
error
is a default level on engine side.
1const evincedService = new EvincedSDK(page);2await page.goto('https://demo.evinced.com/');3const issues = await evincedService.evAnalyze({4 logging: {5 LOGGING_LEVEL: 'debug',6 ADD_LOGGING_CONTEXT: true,7 },8});
Reports Screenshots
The Screenshots feature allows to include screenshots to the Evinced reports. When the feature is enabled, Evinced will take screenshots of the page, highlight an element with a specific accessibility issue, and include them to the report.
false
by default.
Note: The Screenshots feature may affect the test run performance.
Enabling the Screenshots feature
1const issues = await evincedService.evAnalyze({2 enableScreenshots: true,3});
Toggles
Toggles are feature flags which controls SDK and Engine behavior. With toggles we introduce experimental features or manage the way how accessibility issues are gathered. The list of toggles is not specified so we suggest not to rely on specific toggle name or value and use them in investigation purposes only.
Example enabling aXe Best Practices and Needs Review Issues
1const issues = await evincedService.evAnalyze({2 toggles: {3 USE_AXE_NEEDS_REVIEW: true,4 USE_AXE_BEST_PRACTICES: true5 }6});
Skip Validations
Skips specific validations for the given URL pattern and the selector.
SkipValidation[]
by default.
Example
1const skipSelector = {2 selector: 'testValue.slds-checkbox',3 urlRegex: '.*',4 validationTypes: ["WRONG_SEMANTIC_ROLE", "NOT_FOCUSABLE", "NO_DESCRIPTIVE_TEXT"],5};6await evincedService.evStart({ skipValidations: [skipSelector] });
How to identify ID for the specific issue type is described in web reports section.
Knowledge Base Link overrides
This custom parameter helps to customize knowledge base links in the reports. Those links are displayed in the reports as follows:
The knowledge base link can be overridden for every issue type ID.
Example
1const issues = await evincedService.evAnalyze({2 issuesContentPerType: {3 "WRONG_SEMANTIC_ROLE": "https://yourKnowlegdeBase.com/"4 }5});
Where WRONG_SEMANTIC_ROLE
is ID of the type of the specific issue. In this example, the corresponding issue type for
this ID is Interactable Role
. How to identify ID for the specific issue type is described in
web reports section.
After passing the type ID and a custom knowledge base link we are set to see it in the reports.
Shadow DOM support
Example
The feature **is not yet implemented** in this SDK. If you'd like to increase the priority of it, please contact support@evinced.com.
IFrames support
When set to true
, the accessibility tests run analysis on iframes that exist inside the page. Default is false
.
For enabling iFrames analysis on a global level edit the evConfig.json
file: add the following parameter to properties on top level.
1includeIframes: true
So it may look like:
1{2 "logging": {3 "LOGGING_LEVEL": "error",4 "ADD_LOGGING_CONTEXT": true5 },6 "includeIframes": true7}
It is also possible to enable iFrames analysis within one session or one analysis:
1// Enable the feature for one Continuos mode session.2await evincedService.evStart({3 includeIframes: true4});56// OR7// Enable the feature for one analysis.8const issues = await evincedService.evAnalyze({9 includeIframes: true10});
Important! The current implementation does not support an analysis of Cross Origin iFrames. Only issues from the iFrames with the origin of the parent iFrame will be gathered.
Proxy
If your organization is using proxy server for outbound communication, you should set up these proxy server settings to the SDK so it can work properly. Setting the proxy details is done as in the following example:
Example
You can configure pages to load over the HTTP(S) proxy or SOCKSv5. Proxy can be either set globally for the entire browser, or for each browser context individually. You can optionally specify username and password for HTTP(S) proxy, you can also specify hosts to bypass proxy for. Here is an example of a global proxy:
1use: {2 proxy: {3 server: 'http://myproxy.com:3128',4 username: 'usr',5 password: 'pwd'6 }7 }
Global Switch
Global Switch allows to disable or enable Evinced functionality. It could be needed, for example, while working on functional tests in your local environment or for running some CI jobs that are not intended to gather information regarding accessibility issues.
When switched off
evStart()
andevSaveFile()
will be bypassed.evStop()
andevAnalyze()
will return an empty report.
true
by default.
Important! Global Switch environment variable overrides the global configuration option.
Switch on/off Evinced functionality in config
In file evConfig.json
add the following parameter to properties on top level.
1switchOn: false
So it may look like:
1{2 "logging": {3 "LOGGING_LEVEL": "error",4 "ADD_LOGGING_CONTEXT": true5 },6 "switchOn": false7}
Switching off Evinced functionality with environment variable
1export EV_SWITCH_ON=false
Uploading reports to Evinced Platform
Introduction
Evinced Platform allows you to seamlessly collect, organize, visualize and monitor Evinced accessibility reports in one place.
In this section, we will guide you through the key functionalities of the upload methods of the accessibility reports from the Evinced SDK to the Evinced Platform,
which was introduced in version 2.14.0
. This upload method is fully compatible with the previous versions of the Evinced SDK API, and is disabled by default.
Enabling upload to Platform
To enable the uploading functionality of accessibility reports to
the Evinced Platform you will need to set the enableUploadToPlatform
feature flag to "true" via global setUploadToPlatformConfig method:
1 import { setUploadToPlatformConfig } from '@evinced/js-playwright-sdk';2 setUploadToPlatformConfig({ enableUploadToPlatform: true });
or you can use the external config evConfing.json and add the values to be loaded. The external config has more precedence if both initialization options are used.
1 "uploadToPlatformOptions": {2 "enableUploadToPlatform": true,3 "setUploadToPlatformDefault": false4 }
Automatic Upload of Reports
Once the enableUploadToPlatform
method is set to "true", then by default all generated reports will be uploaded to the Platform upon calling the evStop()
command.
If you want to change this behavior, set the setUploadToPlatformDefault
feature flag to "false":
1 setUploadToPlatformConfig({ enableUploadToPlatform: true, setUploadToPlatformDefault: false });
If the setUploadToPlatformDefault
is disabled, you can still upload selected reports to the platform.
For that, use the following parameter in the evStop()
command:
1 await evincedService.evStop({ uploadToPlatform: true });
Test names
To facilitate report management and be able to distinguish between different reports on the Platform, use the setTestInfo
method to inform the test name and test class.
It's recommended to do that in the "beforeEach" hook.
1let evincedService;2test.beforeEach(async ({page}, testInfo)=> {3 evincedService = new EvincedSDK(page);4 evincedService.testRunInfo.addLabel({5 testName: testInfo.title,6 })7})
Labels and Custom Fields
You can attach labels and custom fields to your report to enhance readability in the platform. The labels will be added to the uploaded reports. See the following code example of how to set that up:
1let evincedService;2test.beforeEach(async ({page}, testInfo)=> {3 evincedService = new EvincedSDK(page);4 evincedService.testRunInfo.customLabel({5 'anyCustomParameter': 'demo value',6 productVersion: '0.0.1'7 })8})
Use of beforeEach and afterEach Hooks
We recommend using the beforeEach and afterEach hooks to control analysis sessions and upload reports to the platform. This way, each test will be uploaded separately with its own report.
In beforeEach hook use evStart()
to start Evinced analysis and set any labels you want for the report to contain when uploading to the platform.
In the afterEach hook call evStop()
to stop analysis and upload reports to the platform. See this code example:
1 let evincedService;2 test.beforeEach(async ({page}, testInfo)=>{3 const evincedService = new EvincedSDK(page);4 evincedService.testRunInfo.addLabel({5 testName: testInfo.title,6 testFile: testInfo.file,7 environment: 'Development'8 })9 await evincedService.evStart();10 })11 test.afterEach(async ()=>{12 await evincedService.evStop();13 })
Putting all of this together
Here is a complete code snippet of how to perform uploads to the platform on a per-test basis.
1import { EvincedSDK, setUploadToPlatformConfig } from '@evinced/js-playwright-sdk';23test.describe('Upload demo', () => {4 let evincedService;5 test.beforeAll(() => {6 setUploadToPlatformConfig({ enableUploadToPlatform: true });7 })8 test.beforeEach(async ({page}, testInfo)=>{9 evincedService = new EvincedSDK(page);10 evincedService.testRunInfo.addLabel({11 testName: testInfo.title,12 testFile: testInfo.file,13 environment: 'Development'14 })15 await evincedService.evStart();16 })1718 test.afterEach(async ()=>{19 await evincedService.evStop();20 })2122 test('Upload example playwright #0', async ({ page }) => {23 await page.goto("https://demo.evinced.com");24 });25})
Tutorials
You can find fully functional example projects on our GitHub
Fail the test if critical issues are found
Here you can see a way of failing your test if critical accessibility issues are found using the Playwright JS SDK.
Using evAnalyze:
1const issues = await evincedService.evAnalyze();23const criticalIssues = issues.filter((issue) => issue.severity.name === 'Critical');4await assert(criticalIssues.length === 0, 'found critical issues');
Using evStart/evStop:
1await evincedService.evStart();2const issues = await evincedService.evStop();34const criticalIssues = issues.filter((issue) => issue.severity.name === 'Critical');5await assert(criticalIssues.length === 0, 'found critical issues');
The criticalIssues
array will contain all the critical issues found during the scan. If the array is not empty, the test will fail on the assertion.
Support
Please feel free to reach out to support@evinced.com with any questions.
FAQ
- Can I configure which validations to run?
Yes, see the configuration section for details on how to configure Axe validations to your needs.
- Can I run tests with Evinced using cloud-based services like Sauce Labs, Perfecto, or BrowserStack?
Yes, we have tested the Evinced SDK on many of these types of cloud-based services and expect no issues.