From cf50e007ce809c78805ea8b89248a7e504df31e9 Mon Sep 17 00:00:00 2001 From: Martin Forstner Date: Sat, 4 Sep 2021 21:54:58 +0200 Subject: [PATCH] fix(eslint,prettier): new configs --- .eslintrc.json | 10 +- .prettierrc.json | 7 +- bin/a11y-sitechecker.ts | 43 +++-- lib/a11y-sitechecker.spec.ts | 71 +++++--- lib/a11y-sitechecker.ts | 15 +- lib/models/analyzed-site.ts | 2 - lib/models/small-ones.ts | 2 +- lib/utils/UniqueSelector.ts | 8 +- lib/utils/accept-cookies.ts | 17 +- lib/utils/analyze-site.ts | 26 +-- lib/utils/analyze-url.ts | 6 +- lib/utils/expose-deep-js.ts | 14 +- lib/utils/helper-functions.ts | 2 +- lib/utils/helper-saving-screenshots.ts | 92 +++++----- lib/utils/is-element-visible.ts | 82 +++++---- lib/utils/login.spec.ts | 62 +++---- .../make-sreenshots-with-errors-borderd.ts | 22 +-- lib/utils/mark-all-tabable-items.ts | 154 ++++++---------- lib/utils/result-functions.ts | 4 +- lib/utils/save-results-to-file.spec.ts | 26 ++- lib/utils/save-results-to-file.ts | 15 +- lib/utils/setup-config.spec.ts | 169 +++++++++--------- lib/utils/setup-config.ts | 30 ++-- lib/utils/setup-siteresult.spec.ts | 84 +++++---- package.json | 2 +- tests/a11y-sitechecker.spec.ts | 2 +- tsconfig.json | 2 +- 27 files changed, 478 insertions(+), 491 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 7552e65..dbd605d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,18 +1,16 @@ { "parser": "@typescript-eslint/parser", // Specifies the ESLint parser "extends": [ + "eslint:recommended", "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin - "prettier" // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettie + "prettier", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier, + "plugin:prettier/recommended" ], "parserOptions": { "ecmaVersion": 2020, // Allows for the parsing of modern ECMAScript features "sourceType": "module" // Allows for the use of imports }, - "settings": { - "angular": { - "version": "detect" // Tells eslint-plugin-react to automatically detect the version of React to use - } - }, + "plugins": ["@typescript-eslint"], "root": true, "env": { "node": true, diff --git a/.prettierrc.json b/.prettierrc.json index 2e8d2d1..6430166 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,7 +1,8 @@ { "semi": true, - "trailingComma": "none", + "trailingComma": "all", "singleQuote": true, - "printWidth": 90, - "tabWidth": 4 + "printWidth": 120, + "tabWidth": 4, + "parser": "typescript" } \ No newline at end of file diff --git a/bin/a11y-sitechecker.ts b/bin/a11y-sitechecker.ts index 4999787..b49a6e7 100644 --- a/bin/a11y-sitechecker.ts +++ b/bin/a11y-sitechecker.ts @@ -9,7 +9,6 @@ import { saveResultsToFile } from '../lib/utils/save-results-to-file'; import { setupAxeConfig, setupConfig } from '../lib/utils/setup-config'; import pkg from '../package.json'; - program .version(pkg.version) .usage('[options] ') @@ -23,31 +22,29 @@ program .parse(process.argv); (async (): Promise => { - const config = setupConfig(program.opts()); - const axeConfig = setupAxeConfig(config); - let retCode = 0; - try { - const results = await entry(config, axeConfig, !program.opts().json); - - for (const [i, sitecheckerResult] of results.entries()) { + const config = setupConfig(program.opts()); + const axeConfig = setupAxeConfig(config); + let retCode = 0; + try { + const results = await entry(config, axeConfig, !program.opts().json); - await saveResultsToFile(config, sitecheckerResult, i); + for (const [i, sitecheckerResult] of results.entries()) { + await saveResultsToFile(config, sitecheckerResult, i); - if (sitecheckerResult.violations.length >= config.threshold) { - retCode = 2; - } - } - } catch (e: any) { - if (e.message.includes('Threshold not met')) { + if (sitecheckerResult.violations.length >= config.threshold) { retCode = 2; - } else if (e.message.includes('ERR_NAME_NOT_RESOLVED')) { - retCode = 3; - } else { - retCode = 1; } - - error(e); } - process.exit(retCode); - + } catch (e: any) { + if (e.message.includes('Threshold not met')) { + retCode = 2; + } else if (e.message.includes('ERR_NAME_NOT_RESOLVED')) { + retCode = 3; + } else { + retCode = 1; + } + + error(e); + } + process.exit(retCode); })(); diff --git a/lib/a11y-sitechecker.spec.ts b/lib/a11y-sitechecker.spec.ts index b7bac3b..2a77239 100644 --- a/lib/a11y-sitechecker.spec.ts +++ b/lib/a11y-sitechecker.spec.ts @@ -11,71 +11,84 @@ describe('a11y-sitechecker', () => { const mockConfig: Partial = {}; expect(async () => await entry(mockConfig as Config, {})).toThrowError; }); - + test('1 result with name set', async () => { jest.spyOn(setupConfigMock, 'prepareWorkspace').mockImplementation(() => void 0); jest.spyOn(analyzeSite, 'analyzeSite').mockImplementation(() => new Promise((resolve) => resolve([]))); - jest.spyOn(mergeResults, 'mergeResults').mockImplementation(() => void 0) + jest.spyOn(mergeResults, 'mergeResults').mockImplementation(() => void 0); const mockConfig: Partial = {}; mockConfig.name = 'Testinger'; - mockConfig.viewports = [{width: 100, height: 200}]; + mockConfig.viewports = [{ width: 100, height: 200 }]; mockConfig.json = false; const results = await entry(mockConfig as Config, {}); expect(results[0].name).toBe('Testinger'); expect(results.length).toBe(1); - }); + }); test('Threshold not met', () => { const mockedResults: Partial[] = []; const violation: Partial = {}; jest.spyOn(setupConfigMock, 'prepareWorkspace').mockImplementation(() => void 0); - jest.spyOn(analyzeSite, 'analyzeSite').mockImplementation(() => new Promise((resolve) => resolve(mockedResults as ResultByUrl[]))); - jest.spyOn(mergeResults, 'mergeResults').mockImplementation((result, report) => { - report.violations = [violation as FullCheckerSingleResult]}); + jest.spyOn(analyzeSite, 'analyzeSite').mockImplementation( + () => new Promise((resolve) => resolve(mockedResults as ResultByUrl[])), + ); + jest.spyOn(mergeResults, 'mergeResults').mockImplementation((result, report) => { + report.violations = [violation as FullCheckerSingleResult]; + }); const mockConfig: Partial = {}; mockConfig.name = 'Testinger'; - mockConfig.viewports = [{width: 100, height: 200}]; + mockConfig.viewports = [{ width: 100, height: 200 }]; mockConfig.json = false; mockConfig.threshold = 0; mockConfig.login = undefined; - - return entry(mockConfig as Config, {}).then(e => expect(e.length).toBe(10)).catch(e => expect(e.message).toContain('Threshold not met')); - }); + + return entry(mockConfig as Config, {}) + .then((e) => expect(e.length).toBe(10)) + .catch((e) => expect(e.message).toContain('Threshold not met')); + }); test('Threshold met', () => { const mockedResults: Partial[] = []; const violation: Partial = {}; jest.spyOn(setupConfigMock, 'prepareWorkspace').mockImplementation(() => void 0); - jest.spyOn(analyzeSite, 'analyzeSite').mockImplementation(() => new Promise((resolve) => resolve(mockedResults as ResultByUrl[]))); - jest.spyOn(mergeResults, 'mergeResults').mockImplementation((result, report) => {report.violations = [violation as FullCheckerSingleResult]}); + jest.spyOn(analyzeSite, 'analyzeSite').mockImplementation( + () => new Promise((resolve) => resolve(mockedResults as ResultByUrl[])), + ); + jest.spyOn(mergeResults, 'mergeResults').mockImplementation((result, report) => { + report.violations = [violation as FullCheckerSingleResult]; + }); const mockConfig: Partial = {}; mockConfig.name = 'Testinger'; - mockConfig.viewports = [{width: 100, height: 200}]; + mockConfig.viewports = [{ width: 100, height: 200 }]; mockConfig.json = false; mockConfig.threshold = 2; mockConfig.login = undefined; - - return entry(mockConfig as Config, {}).then(e => expect(e.length).toBe(1)); - }); + + return entry(mockConfig as Config, {}).then((e) => expect(e.length).toBe(1)); + }); test('Write to JSON File', () => { const mockedResults: Partial[] = []; const violation: Partial = {}; jest.spyOn(setupConfigMock, 'prepareWorkspace').mockImplementation(() => void 0); - jest.spyOn(analyzeSite, 'analyzeSite').mockImplementation(() => new Promise((resolve) => resolve(mockedResults as ResultByUrl[]))); - jest.spyOn(mergeResults, 'mergeResults').mockImplementation((result, report) => {report.violations = [violation as FullCheckerSingleResult]}); + jest.spyOn(analyzeSite, 'analyzeSite').mockImplementation( + () => new Promise((resolve) => resolve(mockedResults as ResultByUrl[])), + ); + jest.spyOn(mergeResults, 'mergeResults').mockImplementation((result, report) => { + report.violations = [violation as FullCheckerSingleResult]; + }); const writeToJson = jest.spyOn(helpers, 'writeToJsonFile').mockImplementation(); const mockConfig: Partial = {}; mockConfig.name = 'Testinger'; - mockConfig.viewports = [{width: 100, height: 200}]; + mockConfig.viewports = [{ width: 100, height: 200 }]; mockConfig.json = true; mockConfig.threshold = 2; mockConfig.login = undefined; - - return entry(mockConfig as Config, {}).then(e => { + + return entry(mockConfig as Config, {}).then((e) => { expect(e.length).toBe(1); expect(writeToJson).toBeCalledTimes(1); }); - }); + }); test('throw error in entry', () => { const mockConfig: Partial = {}; @@ -83,9 +96,11 @@ describe('a11y-sitechecker', () => { mockConfig.json = true; mockConfig.threshold = 2; mockConfig.login = undefined; - - return entry(mockConfig as Config, {}).then(e => { - expect(e.length).toBe(10); - }).catch(e => expect(e.message).toContain('Cannot read property')); - }); + + return entry(mockConfig as Config, {}) + .then((e) => { + expect(e.length).toBe(10); + }) + .catch((e) => expect(e.message).toContain('Cannot read property')); + }); }); diff --git a/lib/a11y-sitechecker.ts b/lib/a11y-sitechecker.ts index 5f8a1b3..283f2b0 100644 --- a/lib/a11y-sitechecker.ts +++ b/lib/a11y-sitechecker.ts @@ -9,12 +9,7 @@ import { executeLogin } from './utils/login'; import { mergeResults } from './utils/result-functions'; import { prepareWorkspace } from './utils/setup-config'; - -export async function entry( - config: Config, - axeSpecs: Spec, - onlyReturn?: boolean, -): Promise { +export async function entry(config: Config, axeSpecs: Spec, onlyReturn?: boolean): Promise { try { prepareWorkspace(config); log( @@ -47,8 +42,12 @@ async function checkSite( width: vp.width, height: vp.height, }); - await executeLogin( page, config); - const usedLocale = config.axeConfig?.locale ? config.axeConfig?.locale : (config.axeConfig?.localePath ? config.axeConfig?.localePath : 'en') + await executeLogin(page, config); + const usedLocale = config.axeConfig?.locale + ? config.axeConfig?.locale + : config.axeConfig?.localePath + ? config.axeConfig?.localePath + : 'en'; const result: A11ySitecheckerResult = { testEngine: undefined, diff --git a/lib/models/analyzed-site.ts b/lib/models/analyzed-site.ts index 4404d3a..66a6316 100644 --- a/lib/models/analyzed-site.ts +++ b/lib/models/analyzed-site.ts @@ -1,11 +1,9 @@ - export interface AnalyzedSite { _id: string; url: string; filesByDate: FilesByDate[]; } - export interface FilesByDate { date: Date; files: string[]; diff --git a/lib/models/small-ones.ts b/lib/models/small-ones.ts index 9f7b2a1..8ffa0ca 100644 --- a/lib/models/small-ones.ts +++ b/lib/models/small-ones.ts @@ -9,7 +9,7 @@ export interface ListenerObject { listeners: Event[]; } export interface SpanElement { - elementId: string; + elementId: string; spanId: string; visible: boolean; } diff --git a/lib/utils/UniqueSelector.ts b/lib/utils/UniqueSelector.ts index a9d3773..5b23dae 100644 --- a/lib/utils/UniqueSelector.ts +++ b/lib/utils/UniqueSelector.ts @@ -14,7 +14,13 @@ function uniqueQuery(aSel: string[], dom: JSDOM.JSDOM): boolean { return dom.window.document.querySelectorAll(aSel.join('>')).length === 1; } -function getSelector(aSel: string[], el: Element, sSel: string | undefined, aAttr: string[], dom: JSDOM.JSDOM): boolean { +function getSelector( + aSel: string[], + el: Element, + sSel: string | undefined, + aAttr: string[], + dom: JSDOM.JSDOM, +): boolean { // 1. Check ID first // NOTE: ID must be unique amongst all IDs in an HTML5 document. // https://www.w3.org/TR/html5/dom.html#the-id-attribute diff --git a/lib/utils/accept-cookies.ts b/lib/utils/accept-cookies.ts index c3c65ca..a76772c 100644 --- a/lib/utils/accept-cookies.ts +++ b/lib/utils/accept-cookies.ts @@ -10,16 +10,13 @@ export async function acceptCookieConsent(page: Page, config: Config): Promise { const elements = document.querySelectorAll(cookieSelector); const cookieElements = Array.from(elements).filter((d) => - RegExp(cookieText, 'i').test(d.textContent.trim()) + RegExp(cookieText, 'i').test(d.textContent.trim()), ); console.log(JSON.stringify(cookieElements.length)); if (cookieElements && cookieElements.length > 0) { @@ -34,7 +31,7 @@ export async function acceptCookieConsent(page: Page, config: Config): Promise { const element = document.getElementById(cookieId); @@ -53,11 +50,7 @@ export async function acceptCookieConsent(page: Page, config: Config): Promise, alreadyParsed: string[], notCheckedLinks: string[], - link?: string + link?: string, ): Promise { if (config.crawl === false) { - const resultsAsPromise = lastValueFrom(from(config.urlsToAnalyze) - .pipe( + const resultsAsPromise = lastValueFrom( + from(config.urlsToAnalyze).pipe( mergeMap(async (url) => { const page = await browser.newPage(); const viewport = firstpage.viewport(); - if(viewport) { + if (viewport) { await page.setViewport(viewport); } @@ -39,17 +39,18 @@ export async function analyzeSite( return result; }, 4), toArray(), - )); + ), + ); const result = await resultsAsPromise; resultsByUrl.push(...result); } else { let url; - if(!link) { + if (!link) { url = config.urlsToAnalyze[0]; } else { url = link; } - + if (!url.startsWith('https://') && !url.startsWith('http://')) { url = 'https://' + url; } @@ -72,13 +73,13 @@ export async function analyzeSite( alreadyVisited, ); - const results = lastValueFrom(from(links.entries()) - .pipe( + const results = lastValueFrom( + from(links.entries()).pipe( mergeMap(async ([i, link]) => { log(config.debugMode, 'Visiting ' + i + ' of ' + (links.length - 1)); const page = await browser.newPage(); const viewport = firstpage.viewport(); - if(viewport) { + if (viewport) { await page.setViewport(viewport); } if (alreadyVisited.get(link)) { @@ -89,7 +90,8 @@ export async function analyzeSite( return result; }, 4), toArray(), - )); + ), + ); resultsByUrl.push(...(await results)); if (config.analyzeClicks) @@ -118,7 +120,7 @@ export async function analyzeSite( alreadyVisited, alreadyParsed, notCheckedLinks, - link + link, ); log(config.debugMode, 'Finished analyze of Site: ' + link); } diff --git a/lib/utils/analyze-url.ts b/lib/utils/analyze-url.ts index fedc67d..896865b 100644 --- a/lib/utils/analyze-url.ts +++ b/lib/utils/analyze-url.ts @@ -21,7 +21,7 @@ export async function analyzeUrl( ): Promise { if ((await page.url()) !== url) { await page.goto(url, { waitUntil: 'load' }); - if(config.cookieText && config.cookieSelector) { + if (config.cookieText && config.cookieSelector) { await acceptCookieConsent(page, config); } await waitForHTML(page, config.timeout, config.debugMode); @@ -38,10 +38,10 @@ export async function analyzeUrl( return null; } const viewport = page.viewport(); - if(viewport) { + if (viewport) { alreadyVisited.set(url, viewport); } - + log('Currently analyzing ' + url); if (config.saveImages) { diff --git a/lib/utils/expose-deep-js.ts b/lib/utils/expose-deep-js.ts index d0eb6af..1115bc1 100644 --- a/lib/utils/expose-deep-js.ts +++ b/lib/utils/expose-deep-js.ts @@ -1,9 +1,9 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function exposeDepsJs (deps: Record any>): string { +export function exposeDepsJs(deps: Record any>): string { return Object.keys(deps) - .map((key) => { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - return `window["${key}"] = ${deps[key]};`; - }) - .join("\n"); - }; \ No newline at end of file + .map((key) => { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + return `window["${key}"] = ${deps[key]};`; + }) + .join('\n'); +} diff --git a/lib/utils/helper-functions.ts b/lib/utils/helper-functions.ts index 3f64594..aad4437 100644 --- a/lib/utils/helper-functions.ts +++ b/lib/utils/helper-functions.ts @@ -75,7 +75,7 @@ export function writeToJsonFile(data: string, path: string, vp: SitecheckerViewp log(chalk.blue(`Writing results to ${path}/results_${vp.width}_${vp.height}'.json`)); log(chalk.blue('#############################################################################################')); if (!fs.existsSync(path)) { - fs.mkdirSync(path, {recursive: true}); + fs.mkdirSync(path, { recursive: true }); } fs.writeFileSync(path + '/results_' + vp.width + '_' + vp.height + '.json', data); } diff --git a/lib/utils/helper-saving-screenshots.ts b/lib/utils/helper-saving-screenshots.ts index 4852198..b44401f 100644 --- a/lib/utils/helper-saving-screenshots.ts +++ b/lib/utils/helper-saving-screenshots.ts @@ -8,58 +8,57 @@ export async function saveScreenshotSingleDomElement( saveImage: boolean | undefined, selector: string, padding?: number, - debugMode = false + debugMode = false, ): Promise { if (saveImage) { try { padding = padding || 0; - const rectString: string | undefined = await page.evaluate(async (selector, debugMode) => { - const element = document.querySelector(selector); - if (!element) return undefined; - const elementVisible = window.isElementVisible(selector); - window.debug(debugMode, JSON.stringify(element.getBoundingClientRect())); - if (!elementVisible) { - const oldScrollValues = {x: window.scrollX, y: window.scrollY}; - window.debug( - debugMode, - 'Element not visible. Try to scroll into view' - ); - element.scrollIntoViewIfNeeded(true); - const newScrollValues = {x: window.scrollX, y: window.scrollY}; - window.debug(debugMode, 'Old scroll values ' + JSON.stringify(oldScrollValues) + ' vs new ones: ' + JSON.stringify(newScrollValues)); - - if(oldScrollValues !== newScrollValues) { - await new Promise((resolve) => setTimeout(resolve, 200)); - if(!window.isElementVisible(selector)) { - await new Promise((resolve) => setTimeout(resolve, 2000)); + const rectString: string | undefined = await page.evaluate( + async (selector, debugMode) => { + const element = document.querySelector(selector); + if (!element) return undefined; + const elementVisible = window.isElementVisible(selector); + window.debug(debugMode, JSON.stringify(element.getBoundingClientRect())); + if (!elementVisible) { + const oldScrollValues = { x: window.scrollX, y: window.scrollY }; + window.debug(debugMode, 'Element not visible. Try to scroll into view'); + element.scrollIntoViewIfNeeded(true); + const newScrollValues = { x: window.scrollX, y: window.scrollY }; + window.debug( + debugMode, + 'Old scroll values ' + + JSON.stringify(oldScrollValues) + + ' vs new ones: ' + + JSON.stringify(newScrollValues), + ); + + if (oldScrollValues !== newScrollValues) { + await new Promise((resolve) => setTimeout(resolve, 200)); + if (!window.isElementVisible(selector)) { + await new Promise((resolve) => setTimeout(resolve, 2000)); + } + window.scrollTo(0, 0); + await new Promise((resolve) => setTimeout(resolve, 200)); } - window.scrollTo(0,0); - await new Promise((resolve) => setTimeout(resolve, 200)); + + return JSON.stringify(element.getBoundingClientRect()); } - return JSON.stringify(element.getBoundingClientRect()); - } - return JSON.stringify(element.getBoundingClientRect()); - }, selector, debugMode); + }, + selector, + debugMode, + ); if (!rectString) { - log( - 'Screenshot Single Element failed, because selector (' + - selector + - ') not found' - ); + log('Screenshot Single Element failed, because selector (' + selector + ') not found'); return false; } else { const rect = JSON.parse(rectString); - if(rect.width <= 0 || rect.height <= 0) { - log( - 'Screenshot Single Element failed, because selector (' + - selector + - ') not visible' - ); - return false; + if (rect.width <= 0 || rect.height <= 0) { + log('Screenshot Single Element failed, because selector (' + selector + ') not visible'); + return false; } const pathWithFile = getPathWithFileName(path, fileName); @@ -69,19 +68,19 @@ export async function saveScreenshotSingleDomElement( x: rect.left - padding, y: rect.top - padding, width: rect.width + padding * 2, - height: rect.height + padding * 2 - } + height: rect.height + padding * 2, + }, }); debug(debugMode, pathWithFile + ' saved'); - + return true; - } } catch (error) { log(error + '. Image not saved. Analyze not stopped!'); return false; } - } { + } + { return false; } } @@ -91,7 +90,7 @@ export async function saveScreenshot( path: string | undefined, fileName: string | undefined, saveImage: boolean | undefined, - debugMode = false + debugMode = false, ): Promise { if (saveImage) { try { @@ -108,10 +107,7 @@ export async function saveScreenshot( } } -function getPathWithFileName( - path: string | undefined, - fileName: string | undefined -): string | undefined { +function getPathWithFileName(path: string | undefined, fileName: string | undefined): string | undefined { if (path && fileName) { return path + fileName; } diff --git a/lib/utils/is-element-visible.ts b/lib/utils/is-element-visible.ts index 5bef8ef..2189520 100644 --- a/lib/utils/is-element-visible.ts +++ b/lib/utils/is-element-visible.ts @@ -1,11 +1,11 @@ -// old method +// old method //export function isElementVisible (elementstring: string | null): boolean { // let elementVisible = false; // const dom = elementstring ? document.getElementById(elementstring) : null; // if (dom) { // let currentDom = dom; -import { Point } from "puppeteer"; +import { Point } from 'puppeteer'; // const tolerance = 0.01; // const percentX = 90; @@ -43,41 +43,46 @@ import { Point } from "puppeteer"; export function isElementVisible(elementstring: string | null): boolean { let elem = elementstring ? document.getElementById(elementstring) : null; if (!(elem instanceof Element)) { - elem = elementstring ? document.querySelector(elementstring): null; - if(!elem) return false; + elem = elementstring ? document.querySelector(elementstring) : null; + if (!elem) return false; } const style = getComputedStyle(elem); if (style.display === 'none') return false; if (style.visibility !== 'visible') return false; if (style.opacity === '0') return false; - if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height + - elem.getBoundingClientRect().width === 0) { + if ( + elem.offsetWidth + + elem.offsetHeight + + elem.getBoundingClientRect().height + + elem.getBoundingClientRect().width === + 0 + ) { return false; } - const elementPoints: {[key: string]: Point} = { - 'center': { + const elementPoints: { [key: string]: Point } = { + center: { x: elem.getBoundingClientRect().left + elem.offsetWidth / 2, - y: elem.getBoundingClientRect().top + elem.offsetHeight / 2 + y: elem.getBoundingClientRect().top + elem.offsetHeight / 2, }, 'top-left': { x: elem.getBoundingClientRect().left, - y: elem.getBoundingClientRect().top + y: elem.getBoundingClientRect().top, }, 'top-right': { x: elem.getBoundingClientRect().right, - y: elem.getBoundingClientRect().top + y: elem.getBoundingClientRect().top, }, 'bottom-left': { x: elem.getBoundingClientRect().left, - y: elem.getBoundingClientRect().bottom + y: elem.getBoundingClientRect().bottom, }, 'bottom-right': { x: elem.getBoundingClientRect().right, - y: elem.getBoundingClientRect().bottom - } - } + y: elem.getBoundingClientRect().bottom, + }, + }; - for(const index in elementPoints) { + for (const index in elementPoints) { const point: Point = elementPoints[index]; if (point.x < 0) return false; if (point.x > (document.documentElement.clientWidth || window.innerWidth)) return false; @@ -87,35 +92,38 @@ export function isElementVisible(elementstring: string | null): boolean { if (pointContainer !== null) { do { if (pointContainer === elem) return !elementIntersected(elem.getBoundingClientRect()); - } while (pointContainer = pointContainer.parentNode as HTMLElement); + } while ((pointContainer = pointContainer.parentNode as HTMLElement)); } } return false; } export function elementIntersected(elementRect: DOMRect): boolean { - return Array.from(document.body.getElementsByTagName("*")).filter( - x => getComputedStyle(x, null).getPropertyValue("position") === "fixed" -).some(fixedElem => { - const fixedElementClientRect = fixedElem.getBoundingClientRect(); - const isIntersected = !( - elementRect.top > fixedElementClientRect.bottom || - elementRect.right < fixedElementClientRect.left || - elementRect.bottom < fixedElementClientRect.top || - elementRect.left > fixedElementClientRect.right - ); - if ( isIntersected && fixedElementClientRect.height + elementRect.height > window.adjustScrollingBehindFixed + elementRect.height) { - window.adjustScrollingBehindFixed = fixedElementClientRect.height + elementRect.height - } - return isIntersected; -}) + return Array.from(document.body.getElementsByTagName('*')) + .filter((x) => getComputedStyle(x, null).getPropertyValue('position') === 'fixed') + .some((fixedElem) => { + const fixedElementClientRect = fixedElem.getBoundingClientRect(); + const isIntersected = !( + elementRect.top > fixedElementClientRect.bottom || + elementRect.right < fixedElementClientRect.left || + elementRect.bottom < fixedElementClientRect.top || + elementRect.left > fixedElementClientRect.right + ); + if ( + isIntersected && + fixedElementClientRect.height + elementRect.height > + window.adjustScrollingBehindFixed + elementRect.height + ) { + window.adjustScrollingBehindFixed = fixedElementClientRect.height + elementRect.height; + } + return isIntersected; + }); } - export function highestZIndex(): number { return Math.max( - ...Array.from(document.querySelectorAll('body *'), (elem) => - parseFloat(getComputedStyle(elem).zIndex) - ).filter((zIndex) => !isNaN(zIndex)) + ...Array.from(document.querySelectorAll('body *'), (elem) => parseFloat(getComputedStyle(elem).zIndex)).filter( + (zIndex) => !isNaN(zIndex), + ), ); -}; \ No newline at end of file +} diff --git a/lib/utils/login.spec.ts b/lib/utils/login.spec.ts index cf971ca..45b53cb 100644 --- a/lib/utils/login.spec.ts +++ b/lib/utils/login.spec.ts @@ -4,12 +4,11 @@ import { executeLogin } from './login'; import { cleanUpAfterTest, initBeforeTest } from './test-helper-functions'; describe('login', () => { - let config: Config; - + beforeEach(async () => { config = await initBeforeTest(); - config.timeout = 15000; + config.timeout = 15000; }); afterEach(() => { return cleanUpAfterTest(config); @@ -27,39 +26,42 @@ describe('login', () => { config.login = { url: 'https://www.forsti.eu', steps: [ - { - submit: 'test', - input: [ - { - selector: 'test', - value: 'test', - }, - ], - }, - ]}; + { + submit: 'test', + input: [ + { + selector: 'test', + value: 'test', + }, + ], + }, + ], + }; const browser = await puppeteer.launch(config.launchOptions); const pages = await browser.pages(); - await expect(executeLogin(pages[0], config)).rejects.toThrowError('waiting for selector `test` failed:') + await expect(executeLogin(pages[0], config)).rejects.toThrowError('waiting for selector `test` failed:'); }); test('should not be able to login', async () => { expect.assertions(1); - config.login = {url: "https://www.forsti.eu/wp-admin", - steps: [ - { - submit: '#wp-submit', - input: [ - { - selector: '#user_login', - value: 'test', - }, - { - selector: '#user_pass', - value: 'test', - }, - ], - }, - ]}; + config.login = { + url: 'https://www.forsti.eu/wp-admin', + steps: [ + { + submit: '#wp-submit', + input: [ + { + selector: '#user_login', + value: 'test', + }, + { + selector: '#user_pass', + value: 'test', + }, + ], + }, + ], + }; const browser = await puppeteer.launch(config.launchOptions); const pages = await browser.pages(); await expect(executeLogin(pages[0], config)).resolves.toBe(1); diff --git a/lib/utils/make-sreenshots-with-errors-borderd.ts b/lib/utils/make-sreenshots-with-errors-borderd.ts index 49a0cc1..a312119 100644 --- a/lib/utils/make-sreenshots-with-errors-borderd.ts +++ b/lib/utils/make-sreenshots-with-errors-borderd.ts @@ -4,11 +4,7 @@ import { Config } from '../models/config'; import { debug, error } from './helper-functions'; import { v4 as uuidv4 } from 'uuid'; import { exposeDepsJs } from './expose-deep-js'; -import { - isElementVisible, - elementIntersected, - highestZIndex -} from './is-element-visible'; +import { isElementVisible, elementIntersected, highestZIndex } from './is-element-visible'; import { saveScreenshotSingleDomElement } from './helper-saving-screenshots'; const uniqueNamePerUrl: Map = new Map(); @@ -27,23 +23,20 @@ export async function makeScreenshotsWithErrorsBorderd( resultByUrl: ResultByUrl, page: Page, config: Config, - savedScreenshotHtmls: string[] + savedScreenshotHtmls: string[], ): Promise { let currentMapObject = uniqueNamePerUrl.get(resultByUrl.url); if (!currentMapObject) { uniqueNamePerUrl.set(resultByUrl.url, { id: uuidv4(), count: 0 }); currentMapObject = uniqueNamePerUrl.get(resultByUrl.url); } - + debug(config.debugMode, 'make screenshots with border'); try { await page.exposeFunction('debug', debug); } catch (e: any) { if (config.debugMode) { - error( - e.message + - '. Ignored because normally it means that function already exposed' - ); + error(e.message + '. Ignored because normally it means that function already exposed'); } } @@ -62,7 +55,7 @@ export async function makeScreenshotsWithErrorsBorderd( config.saveImages, node.target[0], 10, - config.debugMode + config.debugMode, ); if (typeof screenshotResult === 'boolean' && screenshotResult === true) { @@ -75,10 +68,7 @@ export async function makeScreenshotsWithErrorsBorderd( savedScreenshotHtmls.push(node.html); } else { - debug( - config.debugMode, - 'Nothing happend, because already screenshoted: ' + node.html - ); + debug(config.debugMode, 'Nothing happend, because already screenshoted: ' + node.html); } } } diff --git a/lib/utils/mark-all-tabable-items.ts b/lib/utils/mark-all-tabable-items.ts index 07adcc6..7f8f7b4 100644 --- a/lib/utils/mark-all-tabable-items.ts +++ b/lib/utils/mark-all-tabable-items.ts @@ -11,10 +11,8 @@ import { exposeDepsJs } from './expose-deep-js'; declare global { interface Window { debug(debugMode: boolean, message: string, ...optionalParams: unknown[]): void; - isElementVisible ( - dom: string | null - ): boolean; - highestZIndex () : number; + isElementVisible(dom: string | null): boolean; + highestZIndex(): number; } } @@ -22,17 +20,14 @@ export async function markAllTabableItems( page: Page, url: string, config: Config, - urlResult: ResultByUrl + urlResult: ResultByUrl, ): Promise { debug(config.debugMode, 'make screens for tabable items'); try { await page.exposeFunction('debug', debug); } catch (e: any) { if (config.debugMode) { - error( - e.message + - '. Ignored because normally it means that function already exposed' - ); + error(e.message + '. Ignored because normally it means that function already exposed'); } } await page.evaluate(exposeDepsJs({ isElementVisible })); @@ -44,7 +39,7 @@ export async function markAllTabableItems( focusableNonStandardElements: [], elementsByVisibility: [], currentIndex: 0, - spanElements: [] + spanElements: [], }; let oldIndex = -1; const imageId = uuidv4(); @@ -70,17 +65,12 @@ export async function markAllTabableItems( styleSheet.innerText = styles; document.head.appendChild(styleSheet); - const addSpanForTabbingNumber = async ( - element: Element, - i: number - ): Promise => { + const addSpanForTabbingNumber = async (element: Element, i: number): Promise => { const tabNumberSpan = document.createElement('SPAN'); const tabNumberText = document.createTextNode(i.toString()); const elementRect = element.getBoundingClientRect(); tabNumberSpan.appendChild(tabNumberText); - elementsFromEvaluationParsed.elementsByVisibility.push( - element.id - ); + elementsFromEvaluationParsed.elementsByVisibility.push(element.id); tabNumberSpan.classList.add('tabCircleOuter'); @@ -94,12 +84,12 @@ export async function markAllTabableItems( elementRect.top + 'px; z-index: ' + (window.highestZIndex() || 1) + - (isTabVisible ? '' : '; display: none') - ); + (isTabVisible ? '' : '; display: none'), + ); elementsFromEvaluationParsed.spanElements.push({ elementId: element.id, spanId: tabNumberSpan.id, - visible: isTabVisible + visible: isTabVisible, }); document.body.appendChild(tabNumberSpan); }; @@ -108,30 +98,17 @@ export async function markAllTabableItems( if (element.getAttribute('style')) { element.setAttribute( 'style', - element.getAttribute('style') + - '; outline: 5px dotted violet; outline-offset: -5px;' + element.getAttribute('style') + '; outline: 5px dotted violet; outline-offset: -5px;', ); } else { - element.setAttribute( - 'style', - 'outline: 5px dotted violet; outline-offset: -5px;' - ); + element.setAttribute('style', 'outline: 5px dotted violet; outline-offset: -5px;'); } }; const elementsFromEvaluationParsed: ElementsFromEvaluation = JSON.parse(elementsFromEvaluationInput); - const standardTags = [ - 'A', - 'AREA', - 'BUTTON', - 'INPUT', - 'TEXTAREA', - 'SELECT', - 'DETAILS', - 'IFRAME' - ]; + const standardTags = ['A', 'AREA', 'BUTTON', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'IFRAME']; //start normal @@ -139,21 +116,18 @@ export async function markAllTabableItems( window.scrollTo(0, 0); let focusableElements = Array.from( document.querySelectorAll( - 'a[href], area[href], button, input, textarea, select, details, iframe, [tabindex]:not([tabindex^="-"])' - ) + 'a[href], area[href], button, input, textarea, select, details, iframe, [tabindex]:not([tabindex^="-"])', + ), ).filter( (el) => !(el as HTMLElement).hasAttribute('disabled') && el.getClientRects().length > 0 && window.getComputedStyle(el).visibility !== 'hidden' && - el.getAttribute('tabindex') !== '-1' + el.getAttribute('tabindex') !== '-1', ); if (elementsFromEvaluationParsed.elementsByVisibility.length > 0) { focusableElements = focusableElements.filter( - (f) => - !elementsFromEvaluationParsed.elementsByVisibility.includes( - f.id - ) + (f) => !elementsFromEvaluationParsed.elementsByVisibility.includes(f.id), ); } @@ -171,25 +145,15 @@ export async function markAllTabableItems( await addSpanForTabbingNumber(element, tabbingNumber); // element.classList.add('tabcircle'); if (!standardTags.includes(element.tagName.toUpperCase())) { - const spanElement = document.getElementById( - 'span_id' + tabbingNumber - ); + const spanElement = document.getElementById('span_id' + tabbingNumber); if (spanElement) { spanElement.innerHTML = spanElement.innerHTML + 'C'; - elementsFromEvaluationParsed.focusableNonStandardElements.push( - element.id - ); + elementsFromEvaluationParsed.focusableNonStandardElements.push(element.id); } } tabbingNumber++; - if ( - !elementsFromEvaluationParsed.elementsByVisibility.includes( - element.id - ) - ) { - elementsFromEvaluationParsed.elementsByVisibility.push( - element.id - ); + if (!elementsFromEvaluationParsed.elementsByVisibility.includes(element.id)) { + elementsFromEvaluationParsed.elementsByVisibility.push(element.id); } } elementsFromEvaluationParsed.currentIndex = tabbingNumber; @@ -197,12 +161,12 @@ export async function markAllTabableItems( return JSON.stringify(elementsFromEvaluationParsed); }, config.debugMode, - JSON.stringify(elementsFromEvaluation) - ) + JSON.stringify(elementsFromEvaluation), + ), ); if (oldIndex < elementsFromEvaluation.currentIndex) { const screenConfig: VideoOptions = { - aspectRatio: '16:9' + aspectRatio: '16:9', }; const recorder = new PuppeteerScreenRecorder(page, screenConfig); let savePath = config.imagesPath; @@ -211,50 +175,44 @@ export async function markAllTabableItems( } await recorder.start(savePath + imageId + '_' + runs + '.mp4'); - elementsFromEvaluation = JSON.parse(await page.evaluate(async (elementsFromEvaluationInput) => { - let scroll = true; - await new Promise((resolve) => setTimeout(resolve, 2500)); - const elementsFromEvaluationParsed: ElementsFromEvaluation = JSON.parse(elementsFromEvaluationInput); - while (scroll) { - window.scroll(0, window.scrollY + window.innerHeight - window.innerHeight/3); - await new Promise((resolve) => setTimeout(resolve, 500)); - elementsFromEvaluationParsed.spanElements.forEach( - (s) => { + elementsFromEvaluation = JSON.parse( + await page.evaluate(async (elementsFromEvaluationInput) => { + let scroll = true; + await new Promise((resolve) => setTimeout(resolve, 2500)); + const elementsFromEvaluationParsed: ElementsFromEvaluation = + JSON.parse(elementsFromEvaluationInput); + while (scroll) { + window.scroll(0, window.scrollY + window.innerHeight - window.innerHeight / 3); + await new Promise((resolve) => setTimeout(resolve, 500)); + elementsFromEvaluationParsed.spanElements.forEach((s) => { const isVisible = window.isElementVisible(s.elementId); - if(isVisible !== s.visible) { - console.log('Visibility changed ' + s.elementId) + if (isVisible !== s.visible) { + console.log('Visibility changed ' + s.elementId); s.visible = isVisible; document - .getElementById(s.spanId) - ?.setAttribute( - 'style', - 'top: ' + - (window.scrollY + - (document - .getElementById(s.elementId) - ?.getBoundingClientRect() - .top || 0)) + - 'px; left: ' + - document - .getElementById(s.elementId) - ?.getBoundingClientRect().left + - 'px; z-index: ' + - (window.highestZIndex() || 1) + (isVisible ? '' : '; display: none') - ); + .getElementById(s.spanId) + ?.setAttribute( + 'style', + 'top: ' + + (window.scrollY + + (document.getElementById(s.elementId)?.getBoundingClientRect().top || + 0)) + + 'px; left: ' + + document.getElementById(s.elementId)?.getBoundingClientRect().left + + 'px; z-index: ' + + (window.highestZIndex() || 1) + + (isVisible ? '' : '; display: none'), + ); } - + }); + await new Promise((resolve) => setTimeout(resolve, 2000)); + if (window.innerHeight + window.pageYOffset >= document.body.offsetHeight) { + scroll = false; } - ); - await new Promise((resolve) => setTimeout(resolve, 2000)); - if ( - window.innerHeight + window.pageYOffset >= - document.body.offsetHeight - ) { - scroll = false; } - } - return JSON.stringify(elementsFromEvaluationParsed); - }, JSON.stringify(elementsFromEvaluation))); + return JSON.stringify(elementsFromEvaluationParsed); + }, JSON.stringify(elementsFromEvaluation)), + ); await recorder.stop(); urlResult.tabableImages.push(savePath); } diff --git a/lib/utils/result-functions.ts b/lib/utils/result-functions.ts index 7282d79..80369d4 100644 --- a/lib/utils/result-functions.ts +++ b/lib/utils/result-functions.ts @@ -1,7 +1,9 @@ import { A11ySitecheckerResult, FullCheckerSingleResult, - NodeResult, Result, ResultByUrl + NodeResult, + Result, + ResultByUrl, } from '../models/a11y-sitechecker-result'; export function setResult( diff --git a/lib/utils/save-results-to-file.spec.ts b/lib/utils/save-results-to-file.spec.ts index bdb2e40..983eba2 100644 --- a/lib/utils/save-results-to-file.spec.ts +++ b/lib/utils/save-results-to-file.spec.ts @@ -8,34 +8,30 @@ import { SiteResult } from '../models/site-result'; import * as defineExtraTags from '../utils/define-extratags'; import * as setupSiteresult from '../utils/setup-siteresult'; import { saveResultsToFile } from './save-results-to-file'; -jest.mock('uuid',() => ({ - v4: () => '12345' +jest.mock('uuid', () => ({ + v4: () => '12345', })); - describe('save-results-to-file', () => { - - test('setup-axe with standard runners and result types', async () => { + test('setup-axe with standard runners and result types', async () => { // expect.assertions(1); - const mockResult: Partial = {analyzedUrls: []}; + const mockResult: Partial = { analyzedUrls: [] }; jest.spyOn(setupSiteresult, 'setupSiteresult').mockImplementation(() => mockResult as SiteResult); jest.spyOn(defineExtraTags, 'defineExtraTags').mockImplementation(() => void 0); // uuid.mockResolvedValue("12345"); const writeFileMock = jest.spyOn(fs, 'writeFileSync').mockImplementation(() => void 0); - jest.spyOn(fs, 'readFileSync').mockImplementation((file) => - { - if(file === 'test/results/files.json') - return Buffer.from('[{"_id": "12345", "filesByDate": []}]') - return fs.readFileSync(file)}); + jest.spyOn(fs, 'readFileSync').mockImplementation((file) => { + if (file === 'test/results/files.json') return Buffer.from('[{"_id": "12345", "filesByDate": []}]'); + return fs.readFileSync(file); + }); const config: Partial = {}; - config.resultsPath = "test/results/"; - config.resultsPathPerUrl = "test/results/test.at/" + config.resultsPath = 'test/results/'; + config.resultsPathPerUrl = 'test/results/test.at/'; const sitecheckerResult: Partial = {}; - sitecheckerResult.testEnvironment = {windowHeight: 100, windowWidth: 100} as TestEnvironment; + sitecheckerResult.testEnvironment = { windowHeight: 100, windowWidth: 100 } as TestEnvironment; await saveResultsToFile(config as Config, sitecheckerResult as A11ySitecheckerResult, 0); expect(writeFileMock).toHaveBeenNthCalledWith(1, expect.stringContaining('100_100.json'), expect.anything()); expect(writeFileMock).toHaveBeenCalledTimes(6); }); - }); diff --git a/lib/utils/save-results-to-file.ts b/lib/utils/save-results-to-file.ts index 8ba367d..c451bfa 100644 --- a/lib/utils/save-results-to-file.ts +++ b/lib/utils/save-results-to-file.ts @@ -7,8 +7,11 @@ import { defineExtraTags } from '../utils/define-extratags'; import { setupSiteresult } from '../utils/setup-siteresult'; import { getEscaped, log } from './helper-functions'; - -export async function saveResultsToFile(config: Config, sitecheckerResult: A11ySitecheckerResult, i: number): Promise { +export async function saveResultsToFile( + config: Config, + sitecheckerResult: A11ySitecheckerResult, + i: number, +): Promise { log('#############################################################################################'); log('Updating resultsFolder with file with Current Time Result'); log('#############################################################################################'); @@ -75,7 +78,13 @@ export async function saveResultsToFile(config: Config, sitecheckerResult: A11yS defineExtraTags(sitecheckerResult, config); fs.writeFileSync(fileToSave, JSON.stringify(siteResult, null, 4)); fs.writeFileSync(config.resultsPath + 'files.json', JSON.stringify(fileObject, null, 4)); - const basePath = config.resultsPathPerUrl + getEscaped(id) + '_' + sitecheckerResult.testEnvironment?.windowWidth + '_' + sitecheckerResult.testEnvironment?.windowHeight; + const basePath = + config.resultsPathPerUrl + + getEscaped(id) + + '_' + + sitecheckerResult.testEnvironment?.windowWidth + + '_' + + sitecheckerResult.testEnvironment?.windowHeight; const violationsPath = basePath + '_violations.json'; const incompletesPath = basePath + '_incompletes.json'; diff --git a/lib/utils/setup-config.spec.ts b/lib/utils/setup-config.spec.ts index d0b7a34..3e122a7 100644 --- a/lib/utils/setup-config.spec.ts +++ b/lib/utils/setup-config.spec.ts @@ -9,161 +9,161 @@ import { prepareWorkspace, setupAxe, setupAxeConfig, setupConfig } from './setup jest.mock('@axe-core/puppeteer'); describe('setup-config', () => { - beforeEach(() => { // Clear all instances and calls to constructor and all methods: (AxePuppeteer as jest.Mock).mockClear(); - }); + }); test('setup-axe with standard runners and result types', async () => { - const config = setupConfig({providedConfig: {name: 'Testinger', urlsToAnalyze: ['www.test.at']}}); + const config = setupConfig({ providedConfig: { name: 'Testinger', urlsToAnalyze: ['www.test.at'] } }); const browser = await puppeteer.launch(config.launchOptions); const page = (await browser.pages())[0]; - await setupAxe(page, {}, config) + await setupAxe(page, {}, config); expect(AxePuppeteer).toHaveBeenCalledTimes(1); const mockAxe = (AxePuppeteer as jest.Mock).mock.instances[0]; expect(mockAxe.configure).toHaveBeenCalledWith({}); - expect(mockAxe.options).toHaveBeenCalledWith({runOnly: ['wcag2aa', 'wcag2a', 'wcag21a', 'wcag21aa', 'best-practice', 'ACT', 'experimental'], resultTypes: ['violations', 'incomplete']}); + expect(mockAxe.options).toHaveBeenCalledWith({ + runOnly: ['wcag2aa', 'wcag2a', 'wcag21a', 'wcag21aa', 'best-practice', 'ACT', 'experimental'], + resultTypes: ['violations', 'incomplete'], + }); }); - test('setup-axe with custon runners and result types', async () => { - const config = setupConfig({config: 'tests/setup-config/config_setup_config.json'}); + test('setup-axe with custon runners and result types', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_setup_config.json' }); const browser = await puppeteer.launch(config.launchOptions); const page = (await browser.pages())[0]; - await setupAxe(page, {}, config) + await setupAxe(page, {}, config); expect(AxePuppeteer).toHaveBeenCalledTimes(1); const mockAxe = (AxePuppeteer as jest.Mock).mock.instances[0]; expect(mockAxe.configure).toHaveBeenCalledWith({}); - expect(mockAxe.options).toHaveBeenCalledWith({runOnly: ['wcag2aa'], resultTypes: ['violations']}); + expect(mockAxe.options).toHaveBeenCalledWith({ runOnly: ['wcag2aa'], resultTypes: ['violations'] }); }); - test('setup-axe-config without axe locales set', async() => { - const config = setupConfig({config: 'tests/setup-config/config_setup_config.json'}); + test('setup-axe-config without axe locales set', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_setup_config.json' }); const axeConfig = setupAxeConfig(config); - expect(axeConfig).toStrictEqual({}) + expect(axeConfig).toStrictEqual({}); }); - test('setup-axe-config with axe de locale as string', async() => { - const config = setupConfig({config: 'tests/setup-config/config_setup_config.json'}); + test('setup-axe-config with axe de locale as string', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_setup_config.json' }); config.axeConfig = {}; - config.axeConfig.locale = "de"; - const axeLocale = JSON.parse( - fs.readFileSync('./node_modules/axe-core/locales/de.json').toString('utf-8'), - ); + config.axeConfig.locale = 'de'; + const axeLocale = JSON.parse(fs.readFileSync('./node_modules/axe-core/locales/de.json').toString('utf-8')); const axeConfig = setupAxeConfig(config); - expect(axeConfig.locale).toStrictEqual(axeLocale) + expect(axeConfig.locale).toStrictEqual(axeLocale); }); - test('setup-axe-config with axe de locale as file', async() => { - const config = setupConfig({config: 'tests/setup-config/config_setup_config.json'}); + test('setup-axe-config with axe de locale as file', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_setup_config.json' }); config.axeConfig = {}; - config.axeConfig.localePath = "./node_modules/axe-core/locales/de.json"; - const axeLocale = JSON.parse( - fs.readFileSync('./node_modules/axe-core/locales/de.json').toString('utf-8'), - ); + config.axeConfig.localePath = './node_modules/axe-core/locales/de.json'; + const axeLocale = JSON.parse(fs.readFileSync('./node_modules/axe-core/locales/de.json').toString('utf-8')); const axeConfig = setupAxeConfig(config); - expect(axeConfig.locale).toStrictEqual(axeLocale) + expect(axeConfig.locale).toStrictEqual(axeLocale); }); - test('setup-axe-config with axe unkown locale as file', async() => { - const config = setupConfig({config: 'tests/setup-config/config_setup_config.json'}); + test('setup-axe-config with axe unkown locale as file', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_setup_config.json' }); config.axeConfig = {}; - config.axeConfig.localePath = "./node_modules/axe-core/locales/de2.json"; + config.axeConfig.localePath = './node_modules/axe-core/locales/de2.json'; const axeConfig = setupAxeConfig(config); - expect(axeConfig).toStrictEqual({}) + expect(axeConfig).toStrictEqual({}); }); - test('setup-axe-config with axe unkown locale as string', async() => { - const config = setupConfig({config: 'tests/setup-config/config_setup_config.json'}); + test('setup-axe-config with axe unkown locale as string', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_setup_config.json' }); config.axeConfig = {}; - config.axeConfig.locale = "./node_modules/axe-core/locales/de2.json"; + config.axeConfig.locale = './node_modules/axe-core/locales/de2.json'; const axeConfig = setupAxeConfig(config); - expect(axeConfig).toStrictEqual({}) + expect(axeConfig).toStrictEqual({}); }); - test('setup-axe-config with axe empty locale as file', async() => { - const config = setupConfig({config: 'tests/setup-config/config_setup_config_empty.json'}); + test('setup-axe-config with axe empty locale as file', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_setup_config_empty.json' }); const axeConfig = setupAxeConfig(config); - expect(axeConfig.locale).toStrictEqual(undefined) + expect(axeConfig.locale).toStrictEqual(undefined); }); - test('prepare-workspace with no folder mentioned', async() => { - const config = setupConfig({config: 'tests/setup-config/config_setup_config.json'}); + test('prepare-workspace with no folder mentioned', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_setup_config.json' }); prepareWorkspace(config); expect(fs.existsSync('results/' + getEscaped(config.name))).toBe(true); - fs.rmdirSync('results', {recursive: true}); + fs.rmdirSync('results', { recursive: true }); }); - test('prepare-workspace with folder mentioned and save images true', async() => { - const config = setupConfig({config: 'tests/setup-config/config_with_images_and_results.json'}); + test('prepare-workspace with folder mentioned and save images true', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_with_images_and_results.json' }); prepareWorkspace(config); expect(fs.existsSync('tests/results/' + getEscaped(config.name))).toBe(true); - expect(fs.existsSync('tests/results/' + getEscaped(config.name)+ '/images')).toBe(true); - fs.rmdirSync('tests/results', {recursive: true}); + expect(fs.existsSync('tests/results/' + getEscaped(config.name) + '/images')).toBe(true); + fs.rmdirSync('tests/results', { recursive: true }); }); - test('prepare-workspace with folder mentioned and save images true and image folder already here', async() => { - const config = setupConfig({config: 'tests/setup-config/config_with_images_and_results.json'}); - if(config.imagesPath) { + test('prepare-workspace with folder mentioned and save images true and image folder already here', async () => { + const config = setupConfig({ config: 'tests/setup-config/config_with_images_and_results.json' }); + if (config.imagesPath) { fs.mkdirSync('tests/results/' + getEscaped(config.name) + '/images', { recursive: true }); expect(fs.existsSync('tests/results/' + getEscaped(config.name) + '/images')).toBe(true); prepareWorkspace(config); expect(fs.existsSync('tests/results/' + getEscaped(config.name))).toBe(true); expect(fs.existsSync('tests/results/' + getEscaped(config.name) + '/images')).toBe(true); - fs.rmdirSync('tests/results', {recursive: true}); + fs.rmdirSync('tests/results', { recursive: true }); } else { fail(); } }); - test('setupConfig with file not created', async() => { - expect(() => setupConfig({config: 'tests/setup-config/config_with_images_and_results2.json'})).toThrowError(/no such file or directory, open/); + test('setupConfig with file not created', async () => { + expect(() => setupConfig({ config: 'tests/setup-config/config_with_images_and_results2.json' })).toThrowError( + /no such file or directory, open/, + ); }); - test('setupConfig all parts are parsed', async() => { - const config = setupConfig({config: 'tests/setup-config/config-all.json'}); - + test('setupConfig all parts are parsed', async () => { + const config = setupConfig({ config: 'tests/setup-config/config-all.json' }); + expect(config).toStrictEqual(expectedConfig); }); - test('setupConfig with threshold', async() => { - const config = setupConfig({config: 'tests/setup-config/config-all.json', threshold: 100}); - const expectedConfigWithThreshold = {...expectedConfig, ...{threshold: 100}}; + test('setupConfig with threshold', async () => { + const config = setupConfig({ config: 'tests/setup-config/config-all.json', threshold: 100 }); + const expectedConfigWithThreshold = { ...expectedConfig, ...{ threshold: 100 } }; expect(config).toStrictEqual(expectedConfigWithThreshold); }); const expectedConfig: Config = { json: true, - resultsPath: "test/results/", + resultsPath: 'test/results/', axeConfig: { - locale: "de", - localePath: "path to locale" + locale: 'de', + localePath: 'path to locale', }, login: { url: 'test.at', steps: [ - { - input: [ - { - selector: "#user_login", - value: "user" - }, - { - selector: "#user_pass", - value: "passwort" - } - ], - "submit": "#wp-submit" - } - ], - }, + { + input: [ + { + selector: '#user_login', + value: 'user', + }, + { + selector: '#user_pass', + value: 'passwort', + }, + ], + submit: '#wp-submit', + }, + ], + }, saveImages: true, imagesPath: 'images', launchOptions: {}, - ignoreElementAttributeValues: ["logout"], - urlsToAnalyze: ["www.test.at"], + ignoreElementAttributeValues: ['logout'], + urlsToAnalyze: ['www.test.at'], analyzeClicks: true, - clickableItemSelector: "button, details", + clickableItemSelector: 'button, details', analyzeClicksWithoutNavigation: true, threshold: 0, timeout: 1000, @@ -171,18 +171,17 @@ describe('setup-config', () => { viewports: [ { width: 1920, - height: 1080 - } + height: 1080, + }, ], - resultTypes: ["violations", "incomplete"], + resultTypes: ['violations', 'incomplete'], resultsPathPerUrl: '', runOnly: ['violations'], idTags: { - "aria-required-attr": ["XYZ"], - "meta-viewport": ["XYZ"] + 'aria-required-attr': ['XYZ'], + 'meta-viewport': ['XYZ'], }, crawl: false, - name: 'Myname' - -}; + name: 'Myname', + }; }); diff --git a/lib/utils/setup-config.ts b/lib/utils/setup-config.ts index e119457..490dc4e 100644 --- a/lib/utils/setup-config.ts +++ b/lib/utils/setup-config.ts @@ -23,10 +23,10 @@ export function setupConfig(options: OptionValues): Config { }, ], resultTypes: ['violations', 'incomplete'], - runOnly: ['wcag2aa', 'wcag2a', 'wcag21a', 'wcag21aa', 'best-practice', 'ACT', 'experimental'], + runOnly: ['wcag2aa', 'wcag2a', 'wcag21a', 'wcag21aa', 'best-practice', 'ACT', 'experimental'], crawl: false, name: '', - urlsToAnalyze: [] + urlsToAnalyze: [], }; config.json = options.json; if (options.threshold) { @@ -35,12 +35,12 @@ export function setupConfig(options: OptionValues): Config { if (options.config || options.providedConfig) { try { let configFile; - if(options.config) { + if (options.config) { configFile = JSON.parse(fs.readFileSync(options.config).toString('utf-8')); } else { configFile = options.providedConfig; } - + if (typeof configFile.json === 'boolean') { config.json = configFile.json; } @@ -72,7 +72,7 @@ export function setupConfig(options: OptionValues): Config { if (configFile.urlsToAnalyze) { config.urlsToAnalyze = configFile.urlsToAnalyze; } else { - throw new Error('It is absolutly necessary to provide 1 or more urls!') + throw new Error('It is absolutly necessary to provide 1 or more urls!'); } if (configFile.analyzeClicksWithoutNavigation) { config.analyzeClicksWithoutNavigation = configFile.analyzeClicksWithoutNavigation; @@ -108,23 +108,23 @@ export function setupConfig(options: OptionValues): Config { config.cookieText = configFile.cookieText; } if (configFile.crawl) { - if(config.urlsToAnalyze?.length === 1 && configFile.crawl) { + if (config.urlsToAnalyze?.length === 1 && configFile.crawl) { config.crawl = true; } else { - throw new Error('Crawling is only possible with one Url supplied!') + throw new Error('Crawling is only possible with one Url supplied!'); } } if (configFile.name) { - config.name = configFile.name; + config.name = configFile.name; } else { - throw new Error('It is absolutly necessary to provide a name!') + throw new Error('It is absolutly necessary to provide a name!'); } } catch (e: any) { error(e); throw e; } } else { - throw new Error('It is absolutly necessary to provide a config!') + throw new Error('It is absolutly necessary to provide a config!'); } return config; @@ -149,14 +149,16 @@ export function setupAxeConfig(config: Config): Spec { const axeConfig: Spec = {}; try { if (config.axeConfig?.locale) { - axeConfig.locale = JSON.parse( - fs.readFileSync('./node_modules/axe-core/locales/' + config.axeConfig.locale + '.json').toString('utf-8'), + axeConfig.locale = JSON.parse( + fs + .readFileSync('./node_modules/axe-core/locales/' + config.axeConfig.locale + '.json') + .toString('utf-8'), ); } else if (config.axeConfig?.localePath) { axeConfig.locale = JSON.parse(fs.readFileSync(config.axeConfig.localePath).toString('utf-8')); } - } catch(e) { - error('Locale not found. Using Standard locale "en"') + } catch (e) { + error('Locale not found. Using Standard locale "en"'); } return axeConfig; } diff --git a/lib/utils/setup-siteresult.spec.ts b/lib/utils/setup-siteresult.spec.ts index c28b1c3..46c1965 100644 --- a/lib/utils/setup-siteresult.spec.ts +++ b/lib/utils/setup-siteresult.spec.ts @@ -1,39 +1,55 @@ -import { FullCheckerSingleResult } from "../models/a11y-sitechecker-result"; -import { setupSiteresult } from "./setup-siteresult"; +import { FullCheckerSingleResult } from '../models/a11y-sitechecker-result'; +import { setupSiteresult } from './setup-siteresult'; describe('setup-siteresult', () => { test('Dummy test', () => { - const result = setupSiteresult('test', {analyzedUrls: [], inapplicable: [testViolation, testViolation], - incomplete: [testViolation], passes: [testViolation], tabableImages: [], testEngine: {name: 'test', version: '1.0'}, - testEnvironment: {userAgent: 'Hello', windowHeight: 100, windowWidth: 100}, testRunner: {name: 'axe'}, toolOptions: {}, timestamp: '', usedLocale: 'de',violations: [testViolation], name: 'url'}) - expect(result).toStrictEqual( - { - analyzedUrls: [], - countInapplicable: 2, - countIncomplete: 1, - countPasses: 1, - countViolations: 1, - id: "test", - tabableImages: [], - testEngine: { - name: "test", - version: "1.0", - }, - testEnvironment: { - userAgent: "Hello", - windowHeight: 100, - windowWidth: 100, - }, - testRunner: { - name: "axe", - }, - timestamp: "", - toolOptions: {}, - name: 'url' - } - ) - }) + const result = setupSiteresult('test', { + analyzedUrls: [], + inapplicable: [testViolation, testViolation], + incomplete: [testViolation], + passes: [testViolation], + tabableImages: [], + testEngine: { name: 'test', version: '1.0' }, + testEnvironment: { userAgent: 'Hello', windowHeight: 100, windowWidth: 100 }, + testRunner: { name: 'axe' }, + toolOptions: {}, + timestamp: '', + usedLocale: 'de', + violations: [testViolation], + name: 'url', + }); + expect(result).toStrictEqual({ + analyzedUrls: [], + countInapplicable: 2, + countIncomplete: 1, + countPasses: 1, + countViolations: 1, + id: 'test', + tabableImages: [], + testEngine: { + name: 'test', + version: '1.0', + }, + testEnvironment: { + userAgent: 'Hello', + windowHeight: 100, + windowWidth: 100, + }, + testRunner: { + name: 'axe', + }, + timestamp: '', + toolOptions: {}, + name: 'url', + }); + }); }); -const testViolation: FullCheckerSingleResult = -{id: 'test', description: 'helloo', help: 'Ist so', nodes: [{targetResult: {target: ['yes'], urls: []}, all: [], any: [], html: '', image: '', none: []}], helpUrl: 'www.test.at',tags: []} \ No newline at end of file +const testViolation: FullCheckerSingleResult = { + id: 'test', + description: 'helloo', + help: 'Ist so', + nodes: [{ targetResult: { target: ['yes'], urls: [] }, all: [], any: [], html: '', image: '', none: [] }], + helpUrl: 'www.test.at', + tags: [], +}; diff --git a/package.json b/package.json index 59b5cd4..e4b2f70 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "scripts": { "build": "tsc && copyfiles README.md dist", "build:prod": "npm run lint && tsc --project tsconfig.prod.json && copyfiles README.md dist", - "lint": "tsc --noEmit && eslint . --ext js,ts,json --quiet --fix", + "lint": "tsc --noEmit && eslint . --ext js,ts --quiet --fix", "semantic-release": "semantic-release", "test": "jest --detectOpenHandles --forceExit", "test:coverage": "jest --coverage --detectOpenHandles", diff --git a/tests/a11y-sitechecker.spec.ts b/tests/a11y-sitechecker.spec.ts index 90fbb94..c68361e 100644 --- a/tests/a11y-sitechecker.spec.ts +++ b/tests/a11y-sitechecker.spec.ts @@ -34,7 +34,7 @@ xdescribe('a11y-sitechecker', () => { config.resultTypes = ['violations', 'inapplicable', 'incomplete', 'passes']; if (config.axeConfig) config.axeConfig.locale = 'de'; const axeConfig = setupAxeConfig(config); - return entry(config, axeConfig, 'forsti.eu', true).then((e) => { + return entry(config, axeConfig, 'forsti.eu', true).then((e) => { expect(e.length).toBe(2); expect(e[0].testEnvironment?.windowHeight).toEqual(1080); expect(e[1].testEnvironment?.windowHeight).toEqual(400); diff --git a/tsconfig.json b/tsconfig.json index 1efad11..ef79a8c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,6 @@ "module": "commonjs", "noImplicitAny": true, "sourceMap": true, - "removeComments": true, "preserveConstEnums": true, "declaration": true, "target": "es2017", @@ -20,6 +19,7 @@ "pretty": true, "noFallthroughCasesInSwitch": true, "allowUnreachableCode": false, + "forceConsistentCasingInFileNames": true, "types": [ "node" ],