Skip to content

Commit

Permalink
feat: new Logic for tabbing images, videos instead screenshots (later…
Browse files Browse the repository at this point in the history
… on both by config)
  • Loading branch information
forsti0506 committed Aug 29, 2021
1 parent d105bf3 commit a9edf61
Show file tree
Hide file tree
Showing 15 changed files with 955 additions and 1,934 deletions.
9 changes: 4 additions & 5 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [

{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\lib\\a11y-sitechecker.js"
"program": "${workspaceFolder}\\bin\\a11y-sitechecker.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"runtimeArgs": ["--preserve-symlinks"],
"args": ["--j", "true"]
}
]
}
2 changes: 1 addition & 1 deletion bin/a11y-sitechecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ program
retCode = 2;
}
}
} catch (e) {
} catch (e: any) {
if (e.message.includes('Threshold not met')) {
retCode = 2;
} else if (e.message.includes('ERR_NAME_NOT_RESOLVED')) {
Expand Down
10 changes: 10 additions & 0 deletions bin/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "Test",
"urlsToAnalyze": [
"https://www.kurier.at"
],
"saveImages": true,
"cookieSelector":"button",
"cookieText":"^(Alle akzeptieren|Akzeptieren|Verstanden|Zustimmen|Okay|OK|Alle Cookies akzeptieren|Einverstanden)$",
"debugMode": true
}
33 changes: 27 additions & 6 deletions lib/a11y-sitechecker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { Config } from './models/config';
import * as analyzeSite from './utils/analyze-site';
import * as helpers from './utils/helper-functions';
import * as mergeResults from './utils/result-functions';
import * as setupConfig from './utils/setup-config';
import * as setupConfigMock from './utils/setup-config';
import { setupAxeConfig, setupConfig } from './utils/setup-config';

describe('a11y-sitechecker', () => {
test('Error on empty config', async () => {
Expand All @@ -14,7 +15,7 @@ describe('a11y-sitechecker', () => {
});

test('1 result with name set', async () => {
jest.spyOn(setupConfig, 'prepareWorkspace').mockImplementation(() => void 0);
jest.spyOn(setupConfigMock, 'prepareWorkspace').mockImplementation(() => void 0);
jest.spyOn(analyzeSite, 'analyzeSite').mockImplementation(() => new Promise((resolve) => resolve([])));
jest.spyOn(mergeResults, 'mergeResults').mockImplementation(() => void 0)
const mockConfig = mock<Config>();
Expand All @@ -29,9 +30,10 @@ describe('a11y-sitechecker', () => {
test('Threshold not met', () => {
const mockedResults: Partial<ResultByUrl>[] = [];
const violation = mockDeep<FullCheckerSingleResult>();
jest.spyOn(setupConfig, 'prepareWorkspace').mockImplementation(() => void 0);
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]});
jest.spyOn(mergeResults, 'mergeResults').mockImplementation((result, report) => { // @ts-ignore
report.violations = [violation]});
const mockConfig = mock<Config>();
mockConfig.name = 'Testinger';
mockConfig.viewports = [{width: 100, height: 200}];
Expand All @@ -44,7 +46,7 @@ describe('a11y-sitechecker', () => {
test('Threshold met', () => {
const mockedResults: Partial<ResultByUrl>[] = [];
const violation = mockDeep<FullCheckerSingleResult>();
jest.spyOn(setupConfig, 'prepareWorkspace').mockImplementation(() => void 0);
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]});
const mockConfig = mock<Config>();
Expand All @@ -60,7 +62,7 @@ describe('a11y-sitechecker', () => {
test('Write to JSON File', () => {
const mockedResults: Partial<ResultByUrl>[] = [];
const violation = mockDeep<FullCheckerSingleResult>();
jest.spyOn(setupConfig, 'prepareWorkspace').mockImplementation(() => void 0);
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]});
const writeToJson = jest.spyOn(helpers, 'writeToJsonFile').mockImplementation();
Expand Down Expand Up @@ -88,4 +90,23 @@ describe('a11y-sitechecker', () => {
expect(e.length).toBe(10);
}).catch(e => expect(e.message).toContain('config.viewports.forEach is not'));
});

test('Error on empty config testinger', () => {
const mockConfig = setupConfig({providedConfig: JSON.parse(`
{
"name": "Test",
"urlsToAnalyze": [
"https://www.kurier.at"
],
"saveImages": true,
"cookieSelector":"button",
"cookieText":"^(Alle akzeptieren|Akzeptieren|Verstanden|Zustimmen|Okay|OK|Alle Cookies akzeptieren|Einverstanden)$",
"debugMode": true
}
`
)});
return entry(mockConfig, setupAxeConfig(mockConfig));
});

});
2 changes: 1 addition & 1 deletion lib/a11y-sitechecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export async function entry(
const promises: Promise<A11ySitecheckerResult>[] = [];
config.viewports.forEach((viewport) => promises.push(checkSite(config, axeSpecs, viewport, onlyReturn)));
return Promise.all(promises);
} catch (err) {
} catch (err: any) {
// Handle any errors
error(err.message);
debug(config.debugMode, err.stackTrace);
Expand Down
13 changes: 7 additions & 6 deletions lib/models/small-ones.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
export interface ElementsFromEvaluation {
elementsByVisibility: ElementVisibility[];
elementsByVisibility: string[];
focusableNonStandardElements: string[];
currentIndex: number;
}

export interface ElementVisibility {
element: string;
visible: boolean;
spanElements: SpanElement[];
}

export interface ListenerObject {
listeners: Event[];
}
export interface SpanElement {
elementId: string;
spanId: string;
visible: boolean;
}
6 changes: 2 additions & 4 deletions lib/utils/accept-cookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ export async function acceptCookieConsent(page: Page, config: Config): Promise<v
const frames = page.frames();
let cookieId;
for(const frame of frames) {
debug(config.debugMode, 'Check frame ' + (frame.name() || frame.url()) + ' for consent button')
cookieId = await frame.evaluate((cookieSelector, cookieText, count) => {
console.log(JSON.stringify(document.body.classList))
const elements = document.querySelectorAll(cookieSelector);
console.log('lengthinger' + elements.length)
const cookieElements = Array.from(elements).filter(d => RegExp(cookieText, 'i').test(d.textContent.trim()))
console.log(JSON.stringify(cookieElements.length))
if (cookieElements && cookieElements.length > 0) {
console.log('okaaay')
const element: HTMLElement = cookieElements[0];
if (!element.id) {
element.setAttribute('id', 'consent_screen_' + count);
Expand All @@ -42,7 +40,7 @@ export async function acceptCookieConsent(page: Page, config: Config): Promise<v
count++;
break;
} else {
debug(config.debugMode, 'Nothing found');
debug(config.debugMode, 'No cookie element found. Iframe Name or Url: ' + (frame.name() || frame.url()));
}
}

Expand Down
9 changes: 9 additions & 0 deletions lib/utils/expose-deep-js.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function exposeDepsJs (deps: Record<string, (...args: any) => any>): string {
return Object.keys(deps)
.map((key) => {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `window["${key}"] = ${deps[key]};`;
})
.join("\n");
};
116 changes: 116 additions & 0 deletions lib/utils/is-element-visible.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// old method
//export function isElementVisible (elementstring: string | null): boolean {
// let elementVisible = false;
// const dom = elementstring ? document.getElementById(elementstring) : null;
// if (dom) {
// let currentDom = dom;

// const tolerance = 0.01;
// const percentX = 90;
// const percentY = 90;

// const elementRect = currentDom.getBoundingClientRect();

// const parentRects: DOMRect[] = [];
// while (currentDom.parentElement != null && currentDom.parentElement.tagName.toUpperCase() !== 'HTML') {
// parentRects.push(
// currentDom.parentElement.getBoundingClientRect()
// );
// currentDom = currentDom.parentElement;
// }
// elementVisible = parentRects.every(function (parentRect) {
// const visiblePixelX =
// Math.min(elementRect.right, parentRect.right) -
// Math.max(elementRect.left, parentRect.left);
// const visiblePixelY =
// Math.min(elementRect.bottom, parentRect.bottom) -
// Math.max(elementRect.top, parentRect.top);
// const visiblePercentageX =
// (visiblePixelX / elementRect.width) * 100;
// const visiblePercentageY =
// (visiblePixelY / elementRect.height) * 100;
// return ( visiblePercentageX + tolerance > percentX &&
// visiblePercentageY + tolerance > percentY &&
// elementRect.top < window.innerHeight &&
// elementRect.bottom >= 0);
// });
// }
// return elementVisible;
// };

export function isElementVisible(elementstring: string | null): boolean {
const elem = elementstring ? document.getElementById(elementstring) : null;
if (!(elem instanceof Element)) 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) {
return false;
}
const elementPoints = {
'center': {
x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
},
'top-left': {
x: elem.getBoundingClientRect().left,
y: elem.getBoundingClientRect().top
},
'top-right': {
x: elem.getBoundingClientRect().right,
y: elem.getBoundingClientRect().top
},
'bottom-left': {
x: elem.getBoundingClientRect().left,
y: elem.getBoundingClientRect().bottom
},
'bottom-right': {
x: elem.getBoundingClientRect().right,
y: elem.getBoundingClientRect().bottom
}
}

for(const index in elementPoints) {
const point = elementPoints[index];
if (point.x < 0) return false;
if (point.x > (document.documentElement.clientWidth || window.innerWidth)) return false;
if (point.y < 0) return false;
if (point.y > (document.documentElement.clientHeight || window.innerHeight)) return false;
let pointContainer = document.elementFromPoint(point.x, point.y);
if (pointContainer !== null) {
do {
if (pointContainer === elem) return !elementIntersected(elem.getBoundingClientRect());
} 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;
})
}


export function highestZIndex(): number {
return Math.max(
...Array.from(document.querySelectorAll('body *'), (elem) =>
parseFloat(getComputedStyle(elem).zIndex)
).filter((zIndex) => !isNaN(zIndex))
);
};
2 changes: 1 addition & 1 deletion lib/utils/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function executeLogin(page: Page, config: Config): Promise<number>
debug(config.debugMode, 'Navigating to url: ' + config.login.url);
await page.goto(config.login.url, { waitUntil: 'networkidle2' });
await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: config.timeout });
} catch (e) {
} catch (e: any) {
error(e);
}
await waitForHTML(page);
Expand Down

0 comments on commit a9edf61

Please sign in to comment.