-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DQA-9426: Create toolkit command to run AXE Scanner (#774)
Co-authored-by: J. João Santos <[email protected]>
- Loading branch information
1 parent
47f456d
commit 5d244e3
Showing
4 changed files
with
199 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,3 +25,6 @@ docs/* | |
!docs/guide/ | ||
docs/guide/* | ||
!docs/guide/*.rst | ||
|
||
# Exclude axe-scan related files. | ||
/axe* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace EcEuropa\Toolkit\TaskRunner\Commands; | ||
|
||
use EcEuropa\Toolkit\TaskRunner\AbstractCommands; | ||
|
||
/** | ||
* Commands to interact with the axe-scan. | ||
* | ||
* @see https://github.com/ttsukagoshi/axe-scan | ||
* @see https://github.com/puppeteer/puppeteer | ||
* @see https://www.deque.com/axe/core-documentation/api-documentation/ | ||
*/ | ||
class AxeCommands extends AbstractCommands | ||
{ | ||
|
||
/** | ||
* A list of dependencies for axe-scan. | ||
* | ||
* @var array|string[] | ||
*/ | ||
private array $dependencies = [ | ||
'libnss3-tools', | ||
'libatk1.0-0', | ||
'libatk-bridge2.0-0', | ||
'libdrm2', | ||
'libxcomposite1', | ||
'libxdamage1', | ||
'libxfixes3', | ||
'libxrandr2', | ||
'libgbm1', | ||
'libxkbcommon-x11-0', | ||
'libpangocairo-1.0-0', | ||
'libasound2', | ||
'fonts-liberation', | ||
'libgcc1', | ||
'libxss1', | ||
'libgtk-3-0', | ||
'libx11-xcb1', | ||
'libxcursor1', | ||
'xdg-utils', | ||
]; | ||
|
||
/** | ||
* Run the axe-scan. | ||
* | ||
* @command toolkit:run-axe-scan | ||
* | ||
* @aliases tk-axe | ||
*/ | ||
public function toolkitRunAxeScan() | ||
{ | ||
$tasks = []; | ||
|
||
$tasks[] = $this->taskExec($this->getBin('run'))->arg('toolkit:setup-axe-scan'); | ||
|
||
$config = $this->getConfigValue('toolkit.axe-scan'); | ||
$exec = $this->taskExec($this->getNodeBinPath('axe-scan')) | ||
->arg('run') | ||
->option('file', $config['file-path']); | ||
|
||
if (!empty($config['allow-list']) && file_exists($config['allow-list'])) { | ||
$exec->option('allowlist', $config['allow-list']); | ||
} | ||
if (!empty($config['result-file'])) { | ||
$exec->rawArg('> ' . $config['result-file']); | ||
} | ||
$tasks[] = $exec; | ||
|
||
if (!empty($config['run-summary'])) { | ||
$tasks[] = $this->taskExec($this->getBin('run'))->arg('toolkit:run-axe-scan-summary'); | ||
} | ||
|
||
return $this->collectionBuilder()->addTaskList($tasks); | ||
} | ||
|
||
/** | ||
* Run the axe-scan summary. | ||
* | ||
* @command toolkit:run-axe-scan-summary | ||
* | ||
* @aliases tk-axe-sum | ||
*/ | ||
public function toolkitRunAxeScanSummary() | ||
{ | ||
$config = $this->getConfigValue('toolkit.axe-scan'); | ||
|
||
$exec = $this->taskExec($this->getNodeBinPath('axe-scan')) | ||
->arg('summary'); | ||
|
||
if (!empty($config['allow-list']) && file_exists($config['allow-list'])) { | ||
$exec->option('allowlist', $config['allow-list']); | ||
} | ||
if (!empty($config['summary-result-file'])) { | ||
$exec->rawArg('> ' . $config['summary-result-file']); | ||
} | ||
return $this->collectionBuilder()->addTask($exec); | ||
} | ||
|
||
/** | ||
* Make sure axe-scan is installed and properly configured. | ||
* | ||
* @command toolkit:setup-axe-scan | ||
*/ | ||
public function toolkitSetupAxeScan() | ||
{ | ||
$tasks = []; | ||
|
||
// Install dependencies if the bin is not present. | ||
if (!file_exists($this->getNodeBinPath('axe-scan'))) { | ||
$tasks[] = $this->taskExecStack() | ||
->exec('npm -v || npm i npm') | ||
->exec('[ -f package.json ] || npm init -y --scope') | ||
->exec('npm list axe-scan && npm update axe-scan || npm install axe-scan -y'); | ||
} | ||
|
||
// Install linux dependencies. | ||
$tasks[] = $this->taskExec($this->getBin('run')) | ||
->arg('toolkit:install-dependencies') | ||
->option('packages', implode(',', $this->dependencies)); | ||
|
||
$config = $this->getConfigValue('toolkit.axe-scan'); | ||
|
||
// Generate the URLs file. | ||
$baseUrl = $this->getConfigValue('drupal.base_url'); | ||
$urls = array_map(function ($url) use ($baseUrl) { | ||
return rtrim($baseUrl, '/') . '/' . ltrim($url, '/'); | ||
}, $config['urls']); | ||
$tasks[] = $this->taskWriteToFile($config['file-path']) | ||
->text(implode(PHP_EOL, $urls)); | ||
|
||
// Generate the config file. | ||
$data = [ | ||
'axeCoreTags' => $config['core-tags'], | ||
'resultTypes' => $config['result-types'], | ||
'filePath' => $config['file-path'], | ||
'locale' => $config['locale'], | ||
]; | ||
$tasks[] = $this->taskWriteToFile($config['config']) | ||
->text(json_encode($data, JSON_PRETTY_PRINT) . PHP_EOL); | ||
|
||
// Apply temporary patch to axe-scan when starting puppeteer to have the | ||
// option --no-sandbox, this avoids the error: Running as root without | ||
// --no-sandbox is not supported. | ||
$files = [ | ||
'node_modules/axe-scan/build/src/commands/run.js', | ||
'node_modules/axe-scan/build/src/commands/summary.js', | ||
]; | ||
$from = 'const browser = await puppeteer.launch();'; | ||
$args = '["--no-sandbox", "--disable-setuid-sandbox", "--single-process", "--disable-impl-side-painting", "--disable-gpu-sandbox", "--disable-accelerated-2d-canvas", "--disable-accelerated-jpeg-decoding", "--disable-dev-shm-usage"]'; | ||
$to = 'const browser = await puppeteer.launch({args: ' . $args . '});'; | ||
foreach ($files as $file) { | ||
if (file_exists($file)) { | ||
$tasks[] = $this->taskReplaceInFile($file)->from($from)->to($to); | ||
} | ||
} | ||
|
||
// Make sure puppeteer is installed. | ||
if (file_exists('node_modules/puppeteer/install.mjs')) { | ||
$tasks[] = $this->taskExec('node node_modules/puppeteer/install.mjs'); | ||
} | ||
|
||
return $this->collectionBuilder()->addTaskList($tasks); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters