From ade839f4351b20a0232188c096942efbf57efbd0 Mon Sep 17 00:00:00 2001 From: Martin Forstner Date: Sat, 27 Feb 2021 12:08:31 +0100 Subject: [PATCH] fix: some small fixes + tests added --- bin/a11y-sitechecker.ts | 3 +- lib/a11y-sitechecker.ts | 8 ++-- lib/models/config.ts | 1 + lib/utils/login.ts | 17 +++++-- lib/utils/setup-config.ts | 20 +++++--- package-lock.json | 27 ++++++----- package.json | 7 ++- tests/a11y-sitechecker-bin.spec.ts | 24 ++++++++++ tests/a11y-sitechecker.spec.ts | 32 +++++++++---- tests/config.json | 5 +- tests/login.spec.ts | 75 ++++++++++++++++++++++++++++++ 11 files changed, 176 insertions(+), 43 deletions(-) create mode 100644 tests/a11y-sitechecker-bin.spec.ts create mode 100644 tests/login.spec.ts diff --git a/bin/a11y-sitechecker.ts b/bin/a11y-sitechecker.ts index fe9ded1..326cdde 100644 --- a/bin/a11y-sitechecker.ts +++ b/bin/a11y-sitechecker.ts @@ -6,14 +6,13 @@ import { entry } from '../lib/a11y-sitechecker'; import { setupAxeConfig, setupConfig } from '../lib/utils/setup-config'; -// Here we're using Commander to specify the CLI options commander .version(pkg.version) .usage('[options] ') .option('-j, --json', 'Output results as JSON. Otherwise output is displayed on the console') .option('--config ', 'Provide a config.json') .option( - '-T, --threshold ', + '--threshold ', 'permit this number of errors, warnings, or notices, otherwise fail with exit code 2', '0', ) diff --git a/lib/a11y-sitechecker.ts b/lib/a11y-sitechecker.ts index 1bbba9f..ed2c0a7 100644 --- a/lib/a11y-sitechecker.ts +++ b/lib/a11y-sitechecker.ts @@ -96,11 +96,11 @@ export async function entry( process.exit(0); } } - } catch (error) { + } catch (err) { // Handle any errors - console.error(error.message); - console.error(error.stackTrace); - process.exit(1); + error(err.message); + debug(err.stackTrace); + throw err; } } diff --git a/lib/models/config.ts b/lib/models/config.ts index 2d41a01..8411d78 100644 --- a/lib/models/config.ts +++ b/lib/models/config.ts @@ -17,6 +17,7 @@ export interface Config { analyzeClicks?: boolean; analyzeClicksWithoutNavigation?: boolean; threshold: number; + timeout: number; } interface LoginStep { diff --git a/lib/utils/login.ts b/lib/utils/login.ts index 6e5cabc..feb8b5d 100644 --- a/lib/utils/login.ts +++ b/lib/utils/login.ts @@ -1,11 +1,12 @@ import { Page } from 'puppeteer'; -import { error, saveScreenshot, waitForHTML } from './helper-functions'; +import { debug, error, saveScreenshot, waitForHTML } from './helper-functions'; import * as chalk from 'chalk'; import { Config } from '../models/config'; -export async function executeLogin(url: string, page: Page, config: Config): Promise { +export async function executeLogin(url: string, page: Page, config: Config): Promise { if (!config.login) { - return; + debug('No Login credentials specified'); + return 0; } let failedLoads = 0; let failed = true; @@ -13,8 +14,9 @@ export async function executeLogin(url: string, page: Page, config: Config): Pro failed = false; try { try { + debug('Navigating to url: ' + url); await page.goto(url, { waitUntil: 'networkidle2' }); - await page.waitForNavigation({ waitUntil: 'networkidle2' }); + await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: config.timeout }); } catch (e) { error(e); } @@ -29,17 +31,22 @@ export async function executeLogin(url: string, page: Page, config: Config): Pro for (const step of config.login) { for (const input of step.input) { - await page.waitForSelector(input.selector); + await page.waitForSelector(input.selector, { timeout: config.timeout }); + debug('Waited for selector ' + input.selector); await page.type(input.selector, input.value); } + debug('Clicking submit: ' + step.submit); await page.click(step.submit); } try { await page.waitForNavigation({ waitUntil: 'networkidle2' }); + debug('Navigation finished: ' + page.url()); await waitForHTML(page); await saveScreenshot(page, config.imagesPath, 'afterLogin.png', config.saveImages); } catch (e) { // eslint-disable-next-line prettier/prettier console.log(chalk.red('No Navigation after Login. Please check if it\'s working as expected!')); } + debug('Finished Login Script: ' + url); + return 1; } diff --git a/lib/utils/setup-config.ts b/lib/utils/setup-config.ts index acad0fe..d34b2d5 100644 --- a/lib/utils/setup-config.ts +++ b/lib/utils/setup-config.ts @@ -2,16 +2,24 @@ import { Spec } from 'axe-core'; import * as fs from 'fs'; import { error, setDebugMode } from './helper-functions'; import { Config } from '../models/config'; +import { OptionValues } from 'commander'; -export function setupConfig(commander): Config { - const config: Config = { json: true, resultsPath: 'results', axeConfig: {}, threshold: 0, imagesPath: 'images' }; - config.json = commander.json; +export function setupConfig(options: OptionValues): Config { + const config: Config = { + json: true, + resultsPath: 'results', + axeConfig: {}, + threshold: 0, + imagesPath: 'images', + timeout: 30, + }; + config.json = options.json; if (!config.threshold) { - config.threshold = parseInt(commander.threshold); + config.threshold = parseInt(options.threshold); } - if (commander.config) { + if (options.config) { try { - const configFile = JSON.parse(fs.readFileSync(commander.config).toString('utf-8')); + const configFile = JSON.parse(fs.readFileSync(options.config).toString('utf-8')); if (typeof configFile.json === 'boolean') { config.json = configFile.json; } diff --git a/package-lock.json b/package-lock.json index 6eec83c..8641445 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "a11y-sitechecker", - "version": "1.6.3", + "version": "1.8.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -791,9 +791,9 @@ "dev": true }, "@types/jasmine": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.3.tgz", - "integrity": "sha512-5QKAG8WfC9XrOgYLXPrxv1G2IIUE6zDyzTWamhNWJO0LqPRUbZ0q0zGHDhDJ7MpFloUuyME/jpBIdPjq3/P3jA==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.4.tgz", + "integrity": "sha512-CTdMERA4iGNcxeqzD7pavb4WLIFq6bGnx6nIJD+1D4Knx24GE6QBPrWVhO8UlIy7gf7rbIt3ZD7iIzryRD2TgA==", "dev": true }, "@types/jsdom": { @@ -1503,9 +1503,9 @@ } }, "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.1.0.tgz", + "integrity": "sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==" }, "compare-func": { "version": "2.0.0", @@ -3548,6 +3548,14 @@ "please-upgrade-node": "^3.2.0", "string-argv": "0.3.1", "stringify-object": "^3.3.0" + }, + "dependencies": { + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + } } }, "listr2": { @@ -7668,11 +7676,6 @@ "has": "^1.0.3" } }, - "odiff": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/odiff/-/odiff-1.4.2.tgz", - "integrity": "sha512-WbmvRE1PogppJhiCujqkSqtwIvA09P2MhPt5Uyo8yRETVbHdezbE9dozwo8BeVasZlnjiqMAQGyFA5zKL4Sa5A==" - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/package.json b/package.json index 8532013..5644318 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "a11y-sitechecker", - "version": "1.6.4", + "version": "1.8.0", "description": "", "repository": { "type": "git", @@ -11,9 +11,8 @@ "dependencies": { "@axe-core/puppeteer": "^4.1.1", "chalk": "^4.1.0", - "commander": "^6.2.1", + "commander": "^7.1.0", "jsdom": "^16.4.0", - "odiff": "^1.4.2", "prettyjson": "^1.2.1", "puppeteer": "^5.5.0", "uuid": "^8.3.2" @@ -26,7 +25,7 @@ "@semantic-release/github": "^7.2.0", "@semantic-release/npm": "^7.0.9", "@semantic-release/release-notes-generator": "^9.0.1", - "@types/jasmine": "^3.6.3", + "@types/jasmine": "^3.6.4", "@types/uuid": "^8.3.0", "@types/jsdom": "^16.2.5", "@types/node": "^14.14.20", diff --git a/tests/a11y-sitechecker-bin.spec.ts b/tests/a11y-sitechecker-bin.spec.ts new file mode 100644 index 0000000..a4b43ab --- /dev/null +++ b/tests/a11y-sitechecker-bin.spec.ts @@ -0,0 +1,24 @@ +import * as process from 'child_process'; + +import 'jasmine'; + +describe('a11y-sitechecker-bin', function () { + it('should be the same url', async (done) => { + const url = 'forsti.eu'; + const job = process.spawn('node', ['./dist/bin/a11y-sitechecker.js', url, '--config=config.json']); + job.stdout.on('data', (data) => { + expect(data.toString()).toContain( + '#############################################################################################', + ); + done(); + }); + + job.stderr.on('data', (data) => { + console.error(`\n${data}`); + }); + + job.on('exit', function (code, signal) { + console.log('child process exited with ' + `code ${code} and signal ${signal}`); + }); + }); +}); diff --git a/tests/a11y-sitechecker.spec.ts b/tests/a11y-sitechecker.spec.ts index a3e29c2..d98e9b8 100644 --- a/tests/a11y-sitechecker.spec.ts +++ b/tests/a11y-sitechecker.spec.ts @@ -1,27 +1,43 @@ import 'jasmine'; - import { setupAxeConfig, setupConfig } from '../lib/utils/setup-config'; import { entry } from '../dist'; -describe('a11y-sitechecker-bin', function () { +describe('a11y-sitechecker', function () { + let config; beforeEach(function () { jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000; - spyOn(process, 'exit').and.stub(); + spyOn(process, 'exit').withArgs(2).and.throwError('Exit with error 2'); + const optionValues = { + json: true, + config: './tests/config.json', + }; + + config = setupConfig(optionValues); }); - it('testcall', async (done) => { - const commander = { json: true, config: './tests/config.json' }; - const config = setupConfig(commander); + it('should be the same url', async (done) => { config.threshold = 1000; if (config.axeConfig) config.axeConfig.locale = 'de'; const axeConfig = setupAxeConfig(config); - entry(config, axeConfig, 'www.forsti.eu').then( + entry(config, axeConfig, 'forsti.eu', true).then((e) => { + expect(e.url).toBe('forsti.eu'); + done(); + }); + }); + + it('should exit with code 2', async (done) => { + config.threshold = 0; + if (config.axeConfig) config.axeConfig.locale = 'de'; + const axeConfig = setupAxeConfig(config); + + entry(config, axeConfig, 'forsti.eu', true).then( (e) => { done(); }, (error) => { - console.log(error); + expect(error.message).toContain('Exit with error 2'); + done(); }, ); }); diff --git a/tests/config.json b/tests/config.json index 9889e0a..65cf512 100644 --- a/tests/config.json +++ b/tests/config.json @@ -1,5 +1,6 @@ { - "json": false, + "json": true, + "imagesPath": "tests/images", "axeConfig": { "locale": "de" }, @@ -12,7 +13,7 @@ }, "debugMode": true, "urlsToAnalyze": [ - "http://www.forsti.eu" + "https://forsti.eu" ], "analyzeClicksWithoutNavigation": false } diff --git a/tests/login.spec.ts b/tests/login.spec.ts new file mode 100644 index 0000000..a99ccf7 --- /dev/null +++ b/tests/login.spec.ts @@ -0,0 +1,75 @@ +import { setupConfig } from '../lib/utils/setup-config'; +import 'jasmine'; +import { executeLogin } from '../lib/utils/login'; +import * as puppeteer from 'puppeteer'; +import { Config } from '../lib/models/config'; +import { Browser } from 'puppeteer'; + +describe('login', function () { + let config: Config; + let browser: Browser; + beforeEach(async function () { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 1200000; + const optionValues = { + json: true, + config: './tests/config.json', + }; + config = setupConfig(optionValues); + config.timeout = 15000; + browser = await puppeteer.launch(config.launchOptions); + const page = (await browser.pages())[0]; + await page.setViewport({ + width: 1920, + height: 1080, + }); + }); + + it('should escape if no login parameter', async (done) => { + executeLogin('https://forsti.eu', (await browser.pages())[0], config).then((retCode) => + expect(retCode).toBe(0), + ); + done(); + }); + + it('should escape if no login parameter', async (done) => { + config.login = [ + { + submit: 'test', + input: [ + { + selector: 'test', + value: 'test', + }, + ], + }, + ]; + + await executeLogin('https://forsti.eu', (await browser.pages())[0], config).catch((err) => { + expect(err.message).toContain('waiting for selector `test` failed:'); + done(); + }); + }); + + it('should not be able to login', async (done) => { + config.login = [ + { + submit: '#wp-submit', + input: [ + { + selector: '#user_login', + value: 'test', + }, + { + selector: '#user_pass', + value: 'test', + }, + ], + }, + ]; + + await executeLogin('https://forsti.eu/wp-admin', (await browser.pages())[0], config).then((r) => { + expect(r).toBe(1); + done(); + }); + }); +});