Playwright JS SDK
The Evinced Playwright JS SDK integrates with new or existing Playwright tests to automatically detect accessibility issues. When adding Evinced functionality to your Playwright project, you can begin to analyze all the pages and DOM changes to offer a dynamic view of how your site can become more accessible. At the conclusion of the test, a rich and comprehensive report is generated to easily track issues to resolution.
Prerequisites
- Playwright version 1.25 or higher
Get Started
Add Evinced as a dependency
In your project directory install the Evinced SDK using npm. The Playwright JS SDK package is not publicly available. [Contact us] to get started!
1npm install @evinced/js-playwright-sdk2npx playwright install
Set licensing environment variables
Put credentials as environment variables:
online credentials: AUTH_SERVICE_ID = 'serviceId' AUTH_SECRET = 'secret'
offline credentials: AUTH_SERVICE_ID = 'serviceId' AUTH_TOKEN = 'token'
Then create the file global-setup.js with code: online credentials:
1import { setCredentials } from '@evinced/playwright-sdk';2async function globalSetup() {3 setCredentials({4 serviceId: process.env.AUTH_SERVICE_ID,5 secret: process.env.AUTH_SECRET,6 });7}8export default globalSetup;
offline credentials:
1import { setOfflineCredentials } from './src/evincedSDK/licenseService';23async function globalSetup() {4 setOfflineCredentials({5 serviceId: process.env.AUTH_SERVICE_ID,6 token: process.env.AUTH_TOKEN,7 });8}9export default globalSetup;
to enable global-setup.js you need to set in the playwright.config.js, just add the following line in config:
1globalSetup: require.resolve('./global-setup')
Your first test
1const { test, expect } = require("@playwright/test");2import { EvincedSDK } from "@evinced/playwright-sdk";3test.describe("Demo Page", () => {4 test("Example Test", async ({ page }) => {5 const evincedService = new EvincedSDK(page);6 await evincedService.evStart();78 await page.goto("https://demo.evinced.com/");910 const issues = await evincedService.evStop();11 evincedService.evSaveFile(12 issues,13 "html",14 "test-results/evinced-report.html"15 );16 });17});
As results of the run you will get evinced-report.html file with an html-report for the visited page
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 be continually scanning 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.
1const { test, expect } = require('@playwright/test');2import { EvincedSDK } from '@evinced/playwright-sdk';34test.describe("Demo Page", () => {5 test('Example Test', async ({ page }) => {6 const evincedService = new EvincedSDK(page);7 await evincedService.evStart();89 await page.goto('https://demo.evinced.com/');1011 const BASE_FORM_SELECTOR = '#gatsby-focus-wrapper > main > div.wrapper-banner > div.filter-container';12 const SELECT_HOME_DROPDOWN = `${BASE_FORM_SELECTOR} > div:nth-child(1) > div > div.dropdown.line`;13 const SELECT_WHERE_DROPDOWN = `${BASE_FORM_SELECTOR} > div:nth-child(2) > div > div.dropdown.line`;14 const TINY_HOME_OPTION = `${BASE_FORM_SELECTOR} > div:nth-child(1) > div > ul > li:nth-child(2)`;15 const EAST_COST_OPTION = `${BASE_FORM_SELECTOR} > div:nth-child(2) > div > ul > li:nth-child(3)`;1617 await page.locator(SELECT_HOME_DROPDOWN).click();18 await page.locator(TINY_HOME_OPTION).click();19 await page.locator(SELECT_WHERE_DROPDOWN).click();20 await page.locator(EAST_COST_OPTION).click();2122 const issues = await evincedService.evStop();23 evincedService.evSaveFile(issues, 'html', 'test-results/evinced-report.html');24 });25});
API
evStart(options)
Continually watches for DOM mutations and page navigation and records all accessibility issues until the evStop(options)
method is called. This method is recommended for dynamic page flows.
evStop(options)
Returns a Report 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 Report
object itself, please see our detailed [Web Reports] page.
evSaveFile(issues, format, destination)
Saves list of the issues in a file, with the specified format and location. The available formats are ‘json
’ and 'html
', for the examples of the reports please see our detailed [Web Reports] page.
evAnalyze(options)
Scans the current page and returns a list of accessibility issues. 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 see our detailed [Web Reports] page. Note: This method is not supported if evStart
is already running.
Axe Config
You can set custom Axe options globally in evConfig.json in the root of project to use your config for each test:
1{2 "axeConfig": {3 "rules": {4 "link-name": { "enabled": false }5 }6 },7 "logging": {8 "LOGGING_LEVEL": "error",9 "ADD_LOGGING_CONTEXT": true10 },11 "skipValidations": []12}
or per individual command in evAnalyze or evStart:
Example enabling aXe Best Practices and Needs Review Issues
1await evincedService.evStart({2 toggles: { USE_AXE_NEEDS_REVIEW: true, USE_AXE_BEST_PRACTICES: true },3})
If provided in both, the command config will be used. For the full Axe config options, see Axe Core API.
Skip validations
It is possible to exclude a selector from results. For this purpose the parameter 'skipValidations' is used.
Values:
selector
is string;
urlRegex
is regex;
validationTypes
- is an array but to ignore types use the value '*'.
Example where the selector for any site and types is excluded. Update evConfig.json:
1{2 "skipValidations": [3 {4 "selector": "testValue.checkbox",5 "urlRegex": ".*",6 "validationTypes": "*"7 }8 ]9}
or use directly form a test: Example where the selector for any site and specified types is excluded.
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] });
Configuration
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
To override the link, add the following section to the evConfig.json
file:
1{2 "issuesContentPerType:": {3 "WRONG_SEMANTIC_ROLE": "https://yourKnowlegdeBase.com/"4 }5}
and below you can see the whole example of the evConfig.json
file:
1{2 "skipValidations": [],3 "toggles": {},4 "sdk_options": {5 "pollingInterval_ms": 500,6 "pollingMaxAttempts": 27 },8 "issuesContentPerType:": {9 "WRONG_SEMANTIC_ROLE": "https://yourKnowlegdeBase.com/"10 }11}
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
tutorials section.
After passing the type ID and a custom knowledge base link we are set to see it in the reports.
Additional ways of use. Code examples
Use beforeAll / afterAll hooks with Playwright JS SDK
A case when the fixture ‘page’ is not called inside the test. It is created as a variable and initialized through the 'browser' fixture. Also, an instance of the class 'EvincedSDK' is initialized at the same time.
This approach allows you to accumulate issues from several tests and produce a single report.
1const { test, expect } = require('@playwright/test');2import { EvincedSDK } from '@evinced/playwright-sdk';34test.describe("Travel Page Demo", () => {5 let evincedService;6 let page;78 test.beforeAll(async ({ browser }) => {9 page = await browser.newPage();10 evincedService = new EvincedSDK(page);11 await evincedService.evStart();12 });1314 test.afterAll(async () => {15 const issues = await evincedService.evStop();16 evincedService.evSaveFile(issues, 'html', 'test-results/evinced-report.html');17 });1819 test('Example Test', async () => {20 await page.goto('https://demo.evinced.com/');21 // page actions22 });23});
Use beforeEach / afterEach hooks with Playwright JS SDK
A case when the fixture ‘page’ is not called inside the test. It is created as a variable and initialized through the 'browser' fixture.
This approach allows you to produce report for each test in the separate file.
1const { test, expect } = require('@playwright/test');2import { EvincedSDK } from '@evinced/playwright-sdk';345test.describe("Travel Page Demo", () => {6 let evincedService;78 test.beforeEach(async ({ page }) => {9 evincedService = new EvincedSDK(page);10 await evincedService.evStart();11 });1213 test.afterEach(async ({},testInfo ) => {14 const issues = await evincedService.evStop();15 const reportName = `${testInfo.title}_${testInfo.status}`;16 evincedService.evSaveFile(issues, 'html', `test-results/${reportName}`);17 });1819 test('Example Test', async ({ page }) => {20 await page.goto('https://demo.evinced.com/');21 // page actions22 });23});
Tutorials
Tutorial 1
Obtaining issues' type IDs for customizing knowledge base links in the reports
The JS Playwright SDK allows to customize issues' knowledge base links in the reports.
To do that, we have to pass a type ID of an issue and a link for substitution to the evConfig.json
file.
More information about the feature you can find in the Knowledge Base Links overrides section.
Below you will find the instruction of how to obtain a type ID of an issue.
Step #1 - Generate a JSON-report
For generating a JSON-report we use the evSaveFile()
command.
In case of using evStart()
command in your tests modify your evStop()
command in the following way:
1const evincedService = new EvincedSDK(page);2await evincedService.evStart();3// do some actions4const issues = await evincedService.evStop();5evincedService.evSaveFile(issues, 'json', "./path/to/save/file");
In case of using evAnalyze()
command in your tests add to the command call the following:
1const evincedService = new EvincedSDK(page);2// do some actions3const issues = await evincedService.evAnalyze();4evincedService.evSaveFile(issues.report, 'json', "./path/to/save/file");
This step is required to be done just once. After obtaining the JSON-report the code mentioned above may be removed if it is not used in the test flow.
Step #2 - Find a required issue in the report
Now that we have everything we need to get an issue type ID. Let's assume we have generated the report as follows:
Click to see code
1[2 {3 "id": "88b997b6-769d-4703-b5e2-62cff68f507f",4 "index": "I1",5 "hidden": false,6 "validation": {7 "id": "80119244-1ea7-4aaa-bb4b-554ad59678c6"8 },9 "skipIssue": false,10 "elements": [11 {12 "id": "42e76fb7-b38b-4979-a18e-0ff0c0d7bbd7",13 "index": "S1",14 "selector": ".block-dropdown:nth-child(1) > .dropholder > .dropdown.line",15 "pageUrl": "https://demo.evinced.com/",16 "boundingBox": {17 "x": 221.21875,18 "y": 576.1875,19 "height": 23.9375,20 "width": 250,21 "top": 576.1875,22 "bottom": 600.125,23 "left": 221.21875,24 "right": 471.2187525 },26 "domSnippet": "<div class=\"dropdown line\"><p>Select</p></div>",27 "componentId": "ce13d0bb"28 }29 ],30 "type": {31 "id": "WRONG_SEMANTIC_ROLE",32 "newId": "86649b9b-96e1-4abb-903a-5ec30f0af526",33 "name": "Interactable Role"34 },35 "firstSeenTime": 1676232893048,36 "lastSeenTime": 1676232893048,37 "additionalInformation": {},38 "summary": "Screen readers do not interpret the element as interactable.",39 "description": "**Cause:** Missing or incorrect semantics prevent screen readers from interpreting an element as interactable. \n**Effect:** Screen reader either does not navigate to the element or navigates to it but does not correctly convey its function to the user. \n**Suggested Solutions:**\n* Change the element's tag to correctly reflect its type, ie: <*button*>, <*a*> or <*input*><*/input*> \n* Add attributes (ARIA) to indicate the element's type and/or potential functionalities. \n",40 "severity": {41 "id": "CRITICAL",42 "name": "Critical",43 "newId": "3c68eb41-9227-481d-8eec-9dc8956b19c4"44 },45 "originalSeverity": {46 "id": "CRITICAL",47 "name": "Critical",48 "newId": "3c68eb41-9227-481d-8eec-9dc8956b19c4"49 },50 "tags": [51 {52 "id": "WCAG-1.3.1",53 "description": "1.3.1 Info and Relationships (Level A)",54 "link": "https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html"55 },56 {57 "id": "WCAG-A"58 },59 {60 "id": "WCAG2.0"61 }62 ],63 "knowledgeBaseLink": "https://knowledge.evinced.com/system-validations/interactable-role",64 "signature": "2ffa5563",65 "canonicalSignature": "2ffa5563"66 }67]
There are entry for each particular issue. For each issue we should see a section called type
where we see a field id
.
In the case above it is WRONG_SEMANTIC_ROLE
. Also in the section we can see a name of the type - Interactable Role
. This is it!
Step #3 - Pass the type ID to the config file
In the Knowledge Base Links overrides section you can find a working example of how to
pass the parameter to the evConfig.json
file.