From af39fadcd8d837bc103f459ea58e4d70cafd3461 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 11:51:42 -0500 Subject: [PATCH 01/65] testing --- .github/actions/find/src/findForUrl.ts | 95 +++++++++++++++++++------- 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 3bcd3fa..57bc942 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -2,32 +2,81 @@ import type { Finding } from './types.d.js'; import AxeBuilder from '@axe-core/playwright' import playwright from 'playwright'; import { AuthContext } from './AuthContext.js'; +import fs from 'fs'; export async function findForUrl(url: string, authContext?: AuthContext): Promise { - const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); - const contextOptions = authContext?.toPlaywrightBrowserContextOptions() ?? {}; - const context = await browser.newContext(contextOptions); - const page = await context.newPage(); - await page.goto(url); - console.log(`Scanning ${page.url()}`); + // const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); + // const contextOptions = authContext?.toPlaywrightBrowserContextOptions() ?? {}; + // const context = await browser.newContext(contextOptions); + // const page = await context.newPage(); + // await page.goto(url); + // console.log(`Scanning ${page.url()}`); + + fs.readdir('../../../../', (err, files) => { + files.forEach(file => { + // will also include directory names + console.log(file); + }); + }); + let findings: Finding[] = []; - try { - const rawFindings = await new AxeBuilder({ page }).analyze(); - findings = rawFindings.violations.map(violation => ({ - scannerType: 'axe', - url, - html: violation.nodes[0].html.replace(/'/g, "'"), - problemShort: violation.help.toLowerCase().replace(/'/g, "'"), - problemUrl: violation.helpUrl.replace(/'/g, "'"), - ruleId: violation.id, - solutionShort: violation.description.toLowerCase().replace(/'/g, "'"), - solutionLong: violation.nodes[0].failureSummary?.replace(/'/g, "'") - })); - } catch (e) { - // do something with the error - } - await context.close(); - await browser.close(); + // try { + // const rawFindings = await new AxeBuilder({ page }).analyze(); + // findings = rawFindings.violations.map(violation => ({ + // scannerType: 'axe', + // url, + // html: violation.nodes[0].html.replace(/'/g, "'"), + // problemShort: violation.help.toLowerCase().replace(/'/g, "'"), + // problemUrl: violation.helpUrl.replace(/'/g, "'"), + // ruleId: violation.id, + // solutionShort: violation.description.toLowerCase().replace(/'/g, "'"), + // solutionLong: violation.nodes[0].failureSummary?.replace(/'/g, "'") + // })); + // } catch (e) { + // // do something with the error + // } + // await context.close(); + // await browser.close(); return findings; } + +interface IPluginContext { + performAxeScan(): Promise; + page: playwright.Page; +} + +// - need to consider that some pages might need +// multiple scans (e.g. scan, click something, scan again) +// - some pages might need to be refreshed to reset the state +// before performing another interaction/scan +// - we also need to be able to report those findings individually +// - maybe each 'scenario' needs a name, because using the url +// alone will not make it clear which scenario the finding came from +class PluginContext implements IPluginContext { + static create({ page }: { page: playwright.Page }): IPluginContext { + const pc = new PluginContext(); + pc.#page = page; + return pc; + } + + #page: playwright.Page; + + async performAxeScan() { + await new AxeBuilder({ page: this.#page }).analyze(); + } + + get page() { + return this.#page; + } + + // - js doesnt know about ts + // - this poJo (plain old js object) is what + // we pass to the plugin + get toPoJo(): IPluginContext { + return { + performAxeScan: this.performAxeScan, + page: this.page, + }; + } +} From 56272edd3c2a9c3caa76292cf3ac0a9bfd74416b Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 12:04:49 -0500 Subject: [PATCH 02/65] testing --- .github/actions/find/src/findForUrl.ts | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 57bc942..25cb0a0 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -12,12 +12,26 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis // await page.goto(url); // console.log(`Scanning ${page.url()}`); - fs.readdir('../../../../', (err, files) => { - files.forEach(file => { - // will also include directory names - console.log(file); + console.log('looking for files in the root directory'); + + try { + fs.readdir('../../../../', (err, files) => { + if (err) { + console.log('error reading dir'); + console.log(err); + } else { + files.forEach(file => { + // will also include directory names + console.log(file); + }); + } }); - }); + } catch (e) { + console.log('error: '); + console.log(e); + } + + console.log('done looking for files'); let findings: Finding[] = []; From 6e30cf0ee45428b43c3beea984da70c917d1351a Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 12:11:22 -0500 Subject: [PATCH 03/65] testing --- .github/actions/find/src/findForUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 25cb0a0..27b3883 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -2,7 +2,7 @@ import type { Finding } from './types.d.js'; import AxeBuilder from '@axe-core/playwright' import playwright from 'playwright'; import { AuthContext } from './AuthContext.js'; -import fs from 'fs'; +import * as fs from 'fs'; export async function findForUrl(url: string, authContext?: AuthContext): Promise { // const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); From 99f629a024e77b89b5147965b9bf31a2420f8c3a Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 12:18:15 -0500 Subject: [PATCH 04/65] test --- .github/actions/find/src/findForUrl.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 27b3883..282245a 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -15,7 +15,7 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis console.log('looking for files in the root directory'); try { - fs.readdir('../../../../', (err, files) => { + fs.readdir('../', (err, files) => { if (err) { console.log('error reading dir'); console.log(err); @@ -69,15 +69,18 @@ interface IPluginContext { // alone will not make it clear which scenario the finding came from class PluginContext implements IPluginContext { static create({ page }: { page: playwright.Page }): IPluginContext { - const pc = new PluginContext(); - pc.#page = page; - return pc; + return new PluginContext({ page }); + } + + constructor({ page }: { page: playwright.Page }) { + this.#page = page; } #page: playwright.Page; async performAxeScan() { - await new AxeBuilder({ page: this.#page }).analyze(); + if (!this.page) return; + await new AxeBuilder({ page: this.page }).analyze(); } get page() { From 28c2f32f7d9def008af3244c4de9f7eb2817ec09 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 12:20:55 -0500 Subject: [PATCH 05/65] test --- .github/actions/find/src/findForUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 282245a..b8f81ef 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -15,7 +15,7 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis console.log('looking for files in the root directory'); try { - fs.readdir('../', (err, files) => { + fs.readdir('../src', (err, files) => { if (err) { console.log('error reading dir'); console.log(err); From 66d0e7b85da5240ee33285fc6e762d0c7cff61c8 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 12:24:46 -0500 Subject: [PATCH 06/65] test --- .github/actions/find/src/findForUrl.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index b8f81ef..db77fda 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -15,16 +15,11 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis console.log('looking for files in the root directory'); try { - fs.readdir('../src', (err, files) => { - if (err) { - console.log('error reading dir'); - console.log(err); - } else { - files.forEach(file => { - // will also include directory names - console.log(file); - }); - } + const res = fs.readdirSync(process.cwd()); + console.log('done reading dir'); + res.forEach(file => { + // will also include directory names + console.log('file: ', file); }); } catch (e) { console.log('error: '); From c18fa605ab4309a785bbd094581b623b79261ac3 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 12:34:34 -0500 Subject: [PATCH 07/65] test --- .github/actions/find/src/findForUrl.ts | 46 +++++++++++++++++--------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index db77fda..518a18e 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -3,6 +3,7 @@ import AxeBuilder from '@axe-core/playwright' import playwright from 'playwright'; import { AuthContext } from './AuthContext.js'; import * as fs from 'fs'; +import * as path from 'path'; export async function findForUrl(url: string, authContext?: AuthContext): Promise { // const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); @@ -12,21 +13,8 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis // await page.goto(url); // console.log(`Scanning ${page.url()}`); - console.log('looking for files in the root directory'); - - try { - const res = fs.readdirSync(process.cwd()); - console.log('done reading dir'); - res.forEach(file => { - // will also include directory names - console.log('file: ', file); - }); - } catch (e) { - console.log('error: '); - console.log(e); - } - - console.log('done looking for files'); + const plugins = await PluginsProvider.getPlugins(); + console.log('number of plugins: ', plugins.length); let findings: Finding[] = []; @@ -50,6 +38,34 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis return findings; } +class PluginsProvider { + static #plugins: any[] = []; + static #pluginsLoaded = false; + + static async getPlugins() { + if (!PluginsProvider.#pluginsLoaded) { + console.log('loading plugins'); + PluginsProvider.#pluginsLoaded = true; + try { + const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); + const res = fs.readdirSync(pluginsDir); + console.log('done reading dir'); + for (const pluginFolder of res) { + // will also include directory names + console.log('pluginFolder: ', pluginFolder); + const indexFile = path.join(pluginsDir, pluginFolder, 'index.js'); + PluginsProvider.#plugins.push(await import(indexFile)); + } + } catch (e) { + console.log('error: '); + console.log(e); + } + } + + return PluginsProvider.#plugins; + } +} + interface IPluginContext { performAxeScan(): Promise; page: playwright.Page; From c1a88ee1121faa67856ba56217ce93e9a5b47c9a Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 12:54:42 -0500 Subject: [PATCH 08/65] try hardcoded string --- .github/actions/find/src/findForUrl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 518a18e..0fb4c80 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -53,8 +53,8 @@ class PluginsProvider { for (const pluginFolder of res) { // will also include directory names console.log('pluginFolder: ', pluginFolder); - const indexFile = path.join(pluginsDir, pluginFolder, 'index.js'); - PluginsProvider.#plugins.push(await import(indexFile)); + const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); + PluginsProvider.#plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js')); } } catch (e) { console.log('error: '); From 4bcbc50b201d98bf50b3d0281f0cddf6fcb5ac30 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 12:58:02 -0500 Subject: [PATCH 09/65] test import --- .github/actions/find/src/findForUrl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 0fb4c80..653273a 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -53,8 +53,8 @@ class PluginsProvider { for (const pluginFolder of res) { // will also include directory names console.log('pluginFolder: ', pluginFolder); - const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); - PluginsProvider.#plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js')); + // const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); + PluginsProvider.#plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); } } catch (e) { console.log('error: '); From 177b498b6f3f9e378a6cd906f32e75d2c60f5db8 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 17 Feb 2026 13:08:02 -0500 Subject: [PATCH 10/65] try require --- .github/actions/find/src/findForUrl.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 653273a..f263837 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -53,8 +53,11 @@ class PluginsProvider { for (const pluginFolder of res) { // will also include directory names console.log('pluginFolder: ', pluginFolder); - // const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); - PluginsProvider.#plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); + const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); + // PluginsProvider.#plugins.push(await require('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); + PluginsProvider.#plugins.push(await require(indexFile)); + + } } catch (e) { console.log('error: '); From 8568551270e4b6db4d7ae3845391ac926d0b1235 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 08:39:32 -0500 Subject: [PATCH 11/65] testing local index file --- .github/actions/find/src/findForUrl.ts | 5 ++--- .github/actions/find/test/index.js | 3 +++ .github/actions/find/test/package.json | 7 +++++++ 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 .github/actions/find/test/index.js create mode 100644 .github/actions/find/test/package.json diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index f263837..5859e29 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -55,9 +55,8 @@ class PluginsProvider { console.log('pluginFolder: ', pluginFolder); const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); // PluginsProvider.#plugins.push(await require('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); - PluginsProvider.#plugins.push(await require(indexFile)); - - + // @ts-ignore + PluginsProvider.#plugins.push(await import('../test/index.js')); } } catch (e) { console.log('error: '); diff --git a/.github/actions/find/test/index.js b/.github/actions/find/test/index.js new file mode 100644 index 0000000..d7f46a5 --- /dev/null +++ b/.github/actions/find/test/index.js @@ -0,0 +1,3 @@ +export default function test() { + console.log('test'); +} diff --git a/.github/actions/find/test/package.json b/.github/actions/find/test/package.json new file mode 100644 index 0000000..39a4403 --- /dev/null +++ b/.github/actions/find/test/package.json @@ -0,0 +1,7 @@ +{ + "name": "plugin-reflow-test", + "version": "1.0.0", + "description": "A plugin to test reflowing of content", + "type": "module", + "main": "index.js" +} From 26f745086e32818cd69029a8a61ea7f7cc7e2e38 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 08:46:47 -0500 Subject: [PATCH 12/65] testing plugin usage --- .github/actions/find/src/findForUrl.ts | 4 ++++ .github/actions/find/test/index.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 5859e29..c4ea3ae 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -14,6 +14,10 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis // console.log(`Scanning ${page.url()}`); const plugins = await PluginsProvider.getPlugins(); + for (const plugin of plugins) { + plugin.default(); + plugin.test2(); + } console.log('number of plugins: ', plugins.length); diff --git a/.github/actions/find/test/index.js b/.github/actions/find/test/index.js index d7f46a5..cc52703 100644 --- a/.github/actions/find/test/index.js +++ b/.github/actions/find/test/index.js @@ -1,3 +1,7 @@ export default function test() { console.log('test'); } + +export function test2() { + console.log('test2'); +} From 920fd853c7e0e74f3aa43287db743f0d3dc4a96e Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 08:51:15 -0500 Subject: [PATCH 13/65] try new plugin folder --- .github/actions/find/src/findForUrl.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index c4ea3ae..1432f9e 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -16,7 +16,7 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis const plugins = await PluginsProvider.getPlugins(); for (const plugin of plugins) { plugin.default(); - plugin.test2(); + // plugin.test2(); } console.log('number of plugins: ', plugins.length); @@ -52,15 +52,15 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const res = fs.readdirSync(pluginsDir); + const res = fs.readdirSync('../../../scanner-plugins/'); console.log('done reading dir'); for (const pluginFolder of res) { // will also include directory names console.log('pluginFolder: ', pluginFolder); - const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); + // const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); // PluginsProvider.#plugins.push(await require('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); // @ts-ignore - PluginsProvider.#plugins.push(await import('../test/index.js')); + PluginsProvider.#plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); } } catch (e) { console.log('error: '); From 0dbb21d798ad741e5a0c1acb5c994bb9f544bdbc Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 08:54:58 -0500 Subject: [PATCH 14/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 1432f9e..6dfb5a1 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -4,6 +4,11 @@ import playwright from 'playwright'; import { AuthContext } from './AuthContext.js'; import * as fs from 'fs'; import * as path from 'path'; +import { fileURLToPath } from 'url'; + +// Helper to get __dirname equivalent in ES Modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); export async function findForUrl(url: string, authContext?: AuthContext): Promise { // const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); @@ -52,7 +57,9 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const res = fs.readdirSync('../../../scanner-plugins/'); + const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins/'); + + const res = fs.readdirSync(absoluteFolderPath); console.log('done reading dir'); for (const pluginFolder of res) { // will also include directory names From 0df238598175d71de6d864e1a779c9ac5495efd7 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 08:58:21 -0500 Subject: [PATCH 15/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 6dfb5a1..89a2726 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -57,7 +57,7 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins/'); + const absoluteFolderPath = path.join(__dirname, '../../scanner-plugins/'); const res = fs.readdirSync(absoluteFolderPath); console.log('done reading dir'); From 276016c13fbf2df8509b509a3bf3f10d21751f16 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:03:47 -0500 Subject: [PATCH 16/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 89a2726..11adbf2 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -57,7 +57,7 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../scanner-plugins/'); + const absoluteFolderPath = path.join(__dirname, '../../../../scanner-plugins/'); const res = fs.readdirSync(absoluteFolderPath); console.log('done reading dir'); From cf05219d57df1619e7df932cb4802819580554b8 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:05:23 -0500 Subject: [PATCH 17/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 11adbf2..9a41559 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -57,10 +57,12 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../../scanner-plugins/'); + const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins/'); const res = fs.readdirSync(absoluteFolderPath); console.log('done reading dir'); + // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/actions/scanner-plugins/' + // '/home/runner/work/_actions/github/accessibility-scanner/current/scanner-plugins/' for (const pluginFolder of res) { // will also include directory names console.log('pluginFolder: ', pluginFolder); From 65807810f9043fbfe229fc5f59274e0254f1e571 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:07:35 -0500 Subject: [PATCH 18/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 9a41559..c1133d1 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -57,12 +57,13 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins/'); + const absoluteFolderPath = path.join(__dirname, '../../scanner-plugins/'); const res = fs.readdirSync(absoluteFolderPath); console.log('done reading dir'); // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/actions/scanner-plugins/' // '/home/runner/work/_actions/github/accessibility-scanner/current/scanner-plugins/' + // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/scanner-plugins/' for (const pluginFolder of res) { // will also include directory names console.log('pluginFolder: ', pluginFolder); From 93609d4f3a9a3faf6df890999deb90f8b2a6f780 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:09:56 -0500 Subject: [PATCH 19/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index c1133d1..08f5099 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -57,13 +57,14 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../scanner-plugins/'); + const absoluteFolderPath = path.join(__dirname, '../../../'); const res = fs.readdirSync(absoluteFolderPath); console.log('done reading dir'); // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/actions/scanner-plugins/' // '/home/runner/work/_actions/github/accessibility-scanner/current/scanner-plugins/' // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/scanner-plugins/' + // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/actions/scanner-plugins/' for (const pluginFolder of res) { // will also include directory names console.log('pluginFolder: ', pluginFolder); From e3aad3e38dffce04874af702d4745ddbf3d51eed Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:11:37 -0500 Subject: [PATCH 20/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 08f5099..5119871 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -71,7 +71,7 @@ class PluginsProvider { // const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); // PluginsProvider.#plugins.push(await require('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); // @ts-ignore - PluginsProvider.#plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); + // PluginsProvider.#plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); } } catch (e) { console.log('error: '); From 05a12add6018a8c0a11f373c0058385d9ac16479 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:14:10 -0500 Subject: [PATCH 21/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 5119871..8670083 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -57,7 +57,7 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../'); + const absoluteFolderPath = path.join(__dirname, '../../../../'); const res = fs.readdirSync(absoluteFolderPath); console.log('done reading dir'); From c61c2ca9d929a97f0aebf0c18ddfe54a55aa58d6 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:16:18 -0500 Subject: [PATCH 22/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 8670083..2bffc61 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -57,7 +57,8 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../../'); + const absoluteFolderPath = path.join(__dirname, '../../../../../'); + console.log('absoluteFolderPath: ', absoluteFolderPath); const res = fs.readdirSync(absoluteFolderPath); console.log('done reading dir'); From b0d19ecf51f22f01f43cd1c888484a1d284c09dc Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:18:01 -0500 Subject: [PATCH 23/65] update local path lookup --- .github/actions/find/src/findForUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 2bffc61..7f598d4 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -57,7 +57,7 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../../../'); + const absoluteFolderPath = path.join(__dirname, '../../../../'); console.log('absoluteFolderPath: ', absoluteFolderPath); const res = fs.readdirSync(absoluteFolderPath); From c6929eb571bfb6a4e34180e57bb74ff8163342cb Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:22:35 -0500 Subject: [PATCH 24/65] add all new content --- .github/scanner-plugins/reflow-test/index.js | 3 +++ scanner-plugins/reflow-test/index.js | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 .github/scanner-plugins/reflow-test/index.js create mode 100644 scanner-plugins/reflow-test/index.js diff --git a/.github/scanner-plugins/reflow-test/index.js b/.github/scanner-plugins/reflow-test/index.js new file mode 100644 index 0000000..a13fed7 --- /dev/null +++ b/.github/scanner-plugins/reflow-test/index.js @@ -0,0 +1,3 @@ +export default function test() { + console.log('reflow test'); +} diff --git a/scanner-plugins/reflow-test/index.js b/scanner-plugins/reflow-test/index.js new file mode 100644 index 0000000..a13fed7 --- /dev/null +++ b/scanner-plugins/reflow-test/index.js @@ -0,0 +1,3 @@ +export default function test() { + console.log('reflow test'); +} From 500fba2f25c2ae0e72e507fad5791482e20bf3fd Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:26:25 -0500 Subject: [PATCH 25/65] log dirs --- .github/actions/find/src/findForUrl.ts | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 7f598d4..9e93f1e 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -18,12 +18,13 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis // await page.goto(url); // console.log(`Scanning ${page.url()}`); - const plugins = await PluginsProvider.getPlugins(); - for (const plugin of plugins) { - plugin.default(); - // plugin.test2(); - } - console.log('number of plugins: ', plugins.length); + // const plugins = await PluginsProvider.getPlugins(); + // for (const plugin of plugins) { + // plugin.default(); + // // plugin.test2(); + // } + // console.log('number of plugins: ', plugins.length); + logDirs(); let findings: Finding[] = []; @@ -47,6 +48,19 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis return findings; } +function logDirs(dirPath?: string) { + const absoluteFolderPath = dirPath ?? path.join(__dirname, '../../../../'); + + if (fs.existsSync(absoluteFolderPath) && fs.lstatSync(absoluteFolderPath).isDirectory()) { + const dirs = fs.readdirSync(absoluteFolderPath); + console.log('Directories in ', absoluteFolderPath, ':'); + dirs.forEach(dir => { + console.log(dir); + logDirs(path.join(absoluteFolderPath, dir)); + }); + } +} + class PluginsProvider { static #plugins: any[] = []; static #pluginsLoaded = false; From aa20c65d6aec831bc0bb6bbda692cfaf0d17e1cf Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:27:19 -0500 Subject: [PATCH 26/65] log dirs --- .github/actions/find/src/findForUrl.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 9e93f1e..9387aaf 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -48,10 +48,17 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis return findings; } +let total = 0; +let max = 1000; function logDirs(dirPath?: string) { const absoluteFolderPath = dirPath ?? path.join(__dirname, '../../../../'); if (fs.existsSync(absoluteFolderPath) && fs.lstatSync(absoluteFolderPath).isDirectory()) { + total++; + if (total > max) { + console.log('max reached, stopping recursion'); + return; + } const dirs = fs.readdirSync(absoluteFolderPath); console.log('Directories in ', absoluteFolderPath, ':'); dirs.forEach(dir => { From b0a666c49bd35cef70cecc1ff803adf8124b70a8 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:30:52 -0500 Subject: [PATCH 27/65] log dirs --- .github/actions/find/src/findForUrl.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 9387aaf..4b3d478 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -51,18 +51,18 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis let total = 0; let max = 1000; function logDirs(dirPath?: string) { + if (total > max) { + console.log('max reached, stopping recursion'); + return; + } const absoluteFolderPath = dirPath ?? path.join(__dirname, '../../../../'); if (fs.existsSync(absoluteFolderPath) && fs.lstatSync(absoluteFolderPath).isDirectory()) { - total++; - if (total > max) { - console.log('max reached, stopping recursion'); - return; - } const dirs = fs.readdirSync(absoluteFolderPath); console.log('Directories in ', absoluteFolderPath, ':'); dirs.forEach(dir => { - console.log(dir); + total++; + if (fs.lstatSync(path.join(absoluteFolderPath, dir)).isDirectory()) console.log(dir); logDirs(path.join(absoluteFolderPath, dir)); }); } From b87a5d86d4a993edf0cfc8bc1951873289a4d44f Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:33:16 -0500 Subject: [PATCH 28/65] log dirs --- .github/actions/find/src/findForUrl.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 4b3d478..8168204 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -55,15 +55,17 @@ function logDirs(dirPath?: string) { console.log('max reached, stopping recursion'); return; } - const absoluteFolderPath = dirPath ?? path.join(__dirname, '../../../../'); + const absoluteFolderPath = dirPath ?? path.join(__dirname, '../../../../../'); if (fs.existsSync(absoluteFolderPath) && fs.lstatSync(absoluteFolderPath).isDirectory()) { const dirs = fs.readdirSync(absoluteFolderPath); console.log('Directories in ', absoluteFolderPath, ':'); dirs.forEach(dir => { total++; - if (fs.lstatSync(path.join(absoluteFolderPath, dir)).isDirectory()) console.log(dir); - logDirs(path.join(absoluteFolderPath, dir)); + if (dir !== 'node_modules') { + if (fs.lstatSync(path.join(absoluteFolderPath, dir)).isDirectory()) console.log(dir); + logDirs(path.join(absoluteFolderPath, dir)); + } }); } } From b46b2e84a551334dcbe35b6ad8442b2843583f30 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 10:39:56 -0500 Subject: [PATCH 29/65] log dirs --- .github/actions/find/src/findForUrl.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 8168204..4c7d808 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -49,7 +49,7 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis } let total = 0; -let max = 1000; +let max = 3000; function logDirs(dirPath?: string) { if (total > max) { console.log('max reached, stopping recursion'); @@ -64,6 +64,11 @@ function logDirs(dirPath?: string) { total++; if (dir !== 'node_modules') { if (fs.lstatSync(path.join(absoluteFolderPath, dir)).isDirectory()) console.log(dir); + } + }); + + dirs.forEach(dir => { + if (dir !== 'node_modules') { logDirs(path.join(absoluteFolderPath, dir)); } }); From bc41a5744d7eb38c068ad8cb5458eadc4560a54f Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 11:09:10 -0500 Subject: [PATCH 30/65] test import --- .github/actions/find/src/findForUrl.ts | 70 +++++++++++++------------- action.yml | 1 + 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 4c7d808..80957cc 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -18,13 +18,13 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis // await page.goto(url); // console.log(`Scanning ${page.url()}`); - // const plugins = await PluginsProvider.getPlugins(); - // for (const plugin of plugins) { - // plugin.default(); - // // plugin.test2(); - // } - // console.log('number of plugins: ', plugins.length); - logDirs(); + const plugins = await PluginsProvider.getPlugins(); + for (const plugin of plugins) { + plugin.default(); + // plugin.test2(); + } + console.log('number of plugins: ', plugins.length); + // logDirs(); let findings: Finding[] = []; @@ -48,32 +48,32 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis return findings; } -let total = 0; -let max = 3000; -function logDirs(dirPath?: string) { - if (total > max) { - console.log('max reached, stopping recursion'); - return; - } - const absoluteFolderPath = dirPath ?? path.join(__dirname, '../../../../../'); - - if (fs.existsSync(absoluteFolderPath) && fs.lstatSync(absoluteFolderPath).isDirectory()) { - const dirs = fs.readdirSync(absoluteFolderPath); - console.log('Directories in ', absoluteFolderPath, ':'); - dirs.forEach(dir => { - total++; - if (dir !== 'node_modules') { - if (fs.lstatSync(path.join(absoluteFolderPath, dir)).isDirectory()) console.log(dir); - } - }); - - dirs.forEach(dir => { - if (dir !== 'node_modules') { - logDirs(path.join(absoluteFolderPath, dir)); - } - }); - } -} +// let total = 0; +// let max = 3000; +// function logDirs(dirPath?: string) { +// if (total > max) { +// console.log('max reached, stopping recursion'); +// return; +// } +// const absoluteFolderPath = dirPath ?? path.join(__dirname, '../../../../../'); + +// if (fs.existsSync(absoluteFolderPath) && fs.lstatSync(absoluteFolderPath).isDirectory()) { +// const dirs = fs.readdirSync(absoluteFolderPath); +// console.log('Directories in ', absoluteFolderPath, ':'); +// dirs.forEach(dir => { +// total++; +// if (dir !== 'node_modules') { +// if (fs.lstatSync(path.join(absoluteFolderPath, dir)).isDirectory()) console.log(dir); +// } +// }); + +// dirs.forEach(dir => { +// if (dir !== 'node_modules') { +// logDirs(path.join(absoluteFolderPath, dir)); +// } +// }); +// } +// } class PluginsProvider { static #plugins: any[] = []; @@ -85,7 +85,7 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../../'); + const absoluteFolderPath = path.join(__dirname, '../../../'); console.log('absoluteFolderPath: ', absoluteFolderPath); const res = fs.readdirSync(absoluteFolderPath); @@ -100,7 +100,7 @@ class PluginsProvider { // const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); // PluginsProvider.#plugins.push(await require('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); // @ts-ignore - // PluginsProvider.#plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); + PluginsProvider.#plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); } } catch (e) { console.log('error: '); diff --git a/action.yml b/action.yml index 3e3ee60..b471772 100644 --- a/action.yml +++ b/action.yml @@ -49,6 +49,7 @@ runs: mkdir -p "${ACTION_DIR}/.github/actions" if [ "$(realpath ".github/actions")" != "$(realpath "${ACTION_DIR}/.github/actions")" ]; then cp -a ".github/actions/." "${ACTION_DIR}/.github/actions/" + cp -a ".github/scanner-plugins/." "${ACTION_DIR}/.github/scanner-plugins/." fi - name: Restore cached results id: restore From 07423903bf3bd61ecf74d7e2c444810ce4900e6a Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 11:11:52 -0500 Subject: [PATCH 31/65] test import --- action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/action.yml b/action.yml index b471772..5cbb563 100644 --- a/action.yml +++ b/action.yml @@ -49,6 +49,10 @@ runs: mkdir -p "${ACTION_DIR}/.github/actions" if [ "$(realpath ".github/actions")" != "$(realpath "${ACTION_DIR}/.github/actions")" ]; then cp -a ".github/actions/." "${ACTION_DIR}/.github/actions/" + fi + + mkdir -p "${ACTION_DIR}/.github/scanner-plugins" + if [ "$(realpath ".github/scanner-plugins")" != "$(realpath "${ACTION_DIR}/.github/scanner-plugins")" ]; then cp -a ".github/scanner-plugins/." "${ACTION_DIR}/.github/scanner-plugins/." fi - name: Restore cached results From 712291ee81a03e513de92ef350e3827ed827fdc8 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 11:15:02 -0500 Subject: [PATCH 32/65] test import --- .github/actions/find/src/findForUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 80957cc..4a498fc 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -85,7 +85,7 @@ class PluginsProvider { PluginsProvider.#pluginsLoaded = true; try { const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../'); + const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); console.log('absoluteFolderPath: ', absoluteFolderPath); const res = fs.readdirSync(absoluteFolderPath); From 774622ab40f9c9b814e13ee001b82b6ce1a4b88e Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 11:25:00 -0500 Subject: [PATCH 33/65] test import --- .github/actions/find/src/findForUrl.ts | 141 ++++--------------- .github/scanner-plugins/reflow-test/index.js | 31 +++- scanner-plugins/reflow-test/index.js | 3 - 3 files changed, 60 insertions(+), 115 deletions(-) delete mode 100644 scanner-plugins/reflow-test/index.js diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 4a498fc..d45114e 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -10,95 +10,57 @@ import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +let findings: Finding[] = []; +function addFinding(findingData: Finding) { + findings.push(findingData); +} + export async function findForUrl(url: string, authContext?: AuthContext): Promise { - // const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); - // const contextOptions = authContext?.toPlaywrightBrowserContextOptions() ?? {}; - // const context = await browser.newContext(contextOptions); - // const page = await context.newPage(); - // await page.goto(url); - // console.log(`Scanning ${page.url()}`); + const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); + const contextOptions = authContext?.toPlaywrightBrowserContextOptions() ?? {}; + const context = await browser.newContext(contextOptions); + const page = await context.newPage(); + await page.goto(url); + console.log(`Scanning ${page.url()}`); const plugins = await PluginsProvider.getPlugins(); for (const plugin of plugins) { - plugin.default(); - // plugin.test2(); + console.log('running plugin: ', plugin.name); + await plugin.default({ page, addFinding }); } - console.log('number of plugins: ', plugins.length); - // logDirs(); - - let findings: Finding[] = []; - // try { - // const rawFindings = await new AxeBuilder({ page }).analyze(); - // findings = rawFindings.violations.map(violation => ({ - // scannerType: 'axe', - // url, - // html: violation.nodes[0].html.replace(/'/g, "'"), - // problemShort: violation.help.toLowerCase().replace(/'/g, "'"), - // problemUrl: violation.helpUrl.replace(/'/g, "'"), - // ruleId: violation.id, - // solutionShort: violation.description.toLowerCase().replace(/'/g, "'"), - // solutionLong: violation.nodes[0].failureSummary?.replace(/'/g, "'") - // })); - // } catch (e) { - // // do something with the error - // } - // await context.close(); - // await browser.close(); + try { + const rawFindings = await new AxeBuilder({ page }).analyze(); + findings = rawFindings.violations.map(violation => ({ + scannerType: 'axe', + url, + html: violation.nodes[0].html.replace(/'/g, "'"), + problemShort: violation.help.toLowerCase().replace(/'/g, "'"), + problemUrl: violation.helpUrl.replace(/'/g, "'"), + ruleId: violation.id, + solutionShort: violation.description.toLowerCase().replace(/'/g, "'"), + solutionLong: violation.nodes[0].failureSummary?.replace(/'/g, "'") + })); + } catch (e) { + // do something with the error + } + await context.close(); + await browser.close(); return findings; } -// let total = 0; -// let max = 3000; -// function logDirs(dirPath?: string) { -// if (total > max) { -// console.log('max reached, stopping recursion'); -// return; -// } -// const absoluteFolderPath = dirPath ?? path.join(__dirname, '../../../../../'); - -// if (fs.existsSync(absoluteFolderPath) && fs.lstatSync(absoluteFolderPath).isDirectory()) { -// const dirs = fs.readdirSync(absoluteFolderPath); -// console.log('Directories in ', absoluteFolderPath, ':'); -// dirs.forEach(dir => { -// total++; -// if (dir !== 'node_modules') { -// if (fs.lstatSync(path.join(absoluteFolderPath, dir)).isDirectory()) console.log(dir); -// } -// }); - -// dirs.forEach(dir => { -// if (dir !== 'node_modules') { -// logDirs(path.join(absoluteFolderPath, dir)); -// } -// }); -// } -// } - class PluginsProvider { static #plugins: any[] = []; static #pluginsLoaded = false; static async getPlugins() { if (!PluginsProvider.#pluginsLoaded) { - console.log('loading plugins'); PluginsProvider.#pluginsLoaded = true; try { - const pluginsDir = path.join(process.cwd(), '.github', 'scanner-plugins'); const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); - console.log('absoluteFolderPath: ', absoluteFolderPath); const res = fs.readdirSync(absoluteFolderPath); - console.log('done reading dir'); - // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/actions/scanner-plugins/' - // '/home/runner/work/_actions/github/accessibility-scanner/current/scanner-plugins/' - // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/scanner-plugins/' - // '/home/runner/work/_actions/github/accessibility-scanner/current/.github/actions/scanner-plugins/' for (const pluginFolder of res) { - // will also include directory names - console.log('pluginFolder: ', pluginFolder); - // const indexFile = path.join('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/reflow-test/index.js'); - // PluginsProvider.#plugins.push(await require('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); // @ts-ignore PluginsProvider.#plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); } @@ -111,46 +73,3 @@ class PluginsProvider { return PluginsProvider.#plugins; } } - -interface IPluginContext { - performAxeScan(): Promise; - page: playwright.Page; -} - -// - need to consider that some pages might need -// multiple scans (e.g. scan, click something, scan again) -// - some pages might need to be refreshed to reset the state -// before performing another interaction/scan -// - we also need to be able to report those findings individually -// - maybe each 'scenario' needs a name, because using the url -// alone will not make it clear which scenario the finding came from -class PluginContext implements IPluginContext { - static create({ page }: { page: playwright.Page }): IPluginContext { - return new PluginContext({ page }); - } - - constructor({ page }: { page: playwright.Page }) { - this.#page = page; - } - - #page: playwright.Page; - - async performAxeScan() { - if (!this.page) return; - await new AxeBuilder({ page: this.page }).analyze(); - } - - get page() { - return this.#page; - } - - // - js doesnt know about ts - // - this poJo (plain old js object) is what - // we pass to the plugin - get toPoJo(): IPluginContext { - return { - performAxeScan: this.performAxeScan, - page: this.page, - }; - } -} diff --git a/.github/scanner-plugins/reflow-test/index.js b/.github/scanner-plugins/reflow-test/index.js index a13fed7..49061ee 100644 --- a/.github/scanner-plugins/reflow-test/index.js +++ b/.github/scanner-plugins/reflow-test/index.js @@ -1,3 +1,32 @@ -export default function test() { +export default async function test({ page, addFinding } = {}) { console.log('reflow test'); + // Check for horizontal scrolling at 320x256 viewport + try { + await page.setViewportSize({ width: 320, height: 256 }); + const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth); + const clientWidth = await page.evaluate(() => document.documentElement.clientWidth); + + // If horizontal scroll is required (with 1px tolerance for rounding) + if (scrollWidth > clientWidth + 1) { + const htmlSnippet = await page.evaluate(() => { + return ``; + }); + + addFinding({ + scannerType: 'viewport', + ruleId: 'horizontal-scroll-320x256', + url, + html: htmlSnippet.replace(/'/g, "'"), + problemShort: 'page requires horizontal scrolling at 320x256 viewport', + problemUrl: 'https://www.w3.org/WAI/WCAG21/Understanding/reflow.html', + solutionShort: 'ensure content is responsive and does not require horizontal scrolling at small viewport sizes', + solutionLong: `The page has a scroll width of ${scrollWidth}px but a client width of only ${clientWidth}px at 320x256 viewport, requiring horizontal scrolling. This violates WCAG 2.1 Level AA Success Criterion 1.4.10 (Reflow).` + }); + } + } catch (e) { + console.error('Error checking horizontal scroll:', e); + } + } + +export const name = 'reflow test'; diff --git a/scanner-plugins/reflow-test/index.js b/scanner-plugins/reflow-test/index.js deleted file mode 100644 index a13fed7..0000000 --- a/scanner-plugins/reflow-test/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function test() { - console.log('reflow test'); -} From 286bb8402f5794db2d3da5683e74d595ae3628b8 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 11:27:13 -0500 Subject: [PATCH 34/65] test reflow plugin --- .github/actions/find/src/findForUrl.ts | 2 +- .github/scanner-plugins/reflow-test/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index d45114e..0f0de9b 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -26,7 +26,7 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis const plugins = await PluginsProvider.getPlugins(); for (const plugin of plugins) { console.log('running plugin: ', plugin.name); - await plugin.default({ page, addFinding }); + await plugin.default({ page, addFinding, url }); } try { diff --git a/.github/scanner-plugins/reflow-test/index.js b/.github/scanner-plugins/reflow-test/index.js index 49061ee..67051f0 100644 --- a/.github/scanner-plugins/reflow-test/index.js +++ b/.github/scanner-plugins/reflow-test/index.js @@ -1,4 +1,4 @@ -export default async function test({ page, addFinding } = {}) { +export default async function test({ page, addFinding, url } = {}) { console.log('reflow test'); // Check for horizontal scrolling at 320x256 viewport try { From 97ca241343a8fe7cc9fd786ffd51931c1154b145 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 11:33:48 -0500 Subject: [PATCH 35/65] move findings back into function --- .github/actions/find/src/findForUrl.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 0f0de9b..e8e540d 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -10,11 +10,6 @@ import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -let findings: Finding[] = []; -function addFinding(findingData: Finding) { - findings.push(findingData); -} - export async function findForUrl(url: string, authContext?: AuthContext): Promise { const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); const contextOptions = authContext?.toPlaywrightBrowserContextOptions() ?? {}; @@ -23,6 +18,11 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis await page.goto(url); console.log(`Scanning ${page.url()}`); + let findings: Finding[] = []; + const addFinding = (findingData: Finding) => { + findings.push(findingData); + }; + const plugins = await PluginsProvider.getPlugins(); for (const plugin of plugins) { console.log('running plugin: ', plugin.name); From babf756737eb240608b7d8f41ee63d79d11206e3 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 11:35:26 -0500 Subject: [PATCH 36/65] remove unused test folder --- .github/actions/find/test/index.js | 7 ------- .github/actions/find/test/package.json | 7 ------- 2 files changed, 14 deletions(-) delete mode 100644 .github/actions/find/test/index.js delete mode 100644 .github/actions/find/test/package.json diff --git a/.github/actions/find/test/index.js b/.github/actions/find/test/index.js deleted file mode 100644 index cc52703..0000000 --- a/.github/actions/find/test/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function test() { - console.log('test'); -} - -export function test2() { - console.log('test2'); -} diff --git a/.github/actions/find/test/package.json b/.github/actions/find/test/package.json deleted file mode 100644 index 39a4403..0000000 --- a/.github/actions/find/test/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "plugin-reflow-test", - "version": "1.0.0", - "description": "A plugin to test reflowing of content", - "type": "module", - "main": "index.js" -} From 626723a42acb2cb607c7868bcfb7bb1aad1cbe32 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 15:02:47 -0500 Subject: [PATCH 37/65] update code structure --- .github/actions/find/src/findForUrl.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index e8e540d..e116933 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -23,7 +23,7 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis findings.push(findingData); }; - const plugins = await PluginsProvider.getPlugins(); + const plugins = await getPlugins(); for (const plugin of plugins) { console.log('running plugin: ', plugin.name); await plugin.default({ page, addFinding, url }); @@ -49,20 +49,19 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis return findings; } -class PluginsProvider { - static #plugins: any[] = []; - static #pluginsLoaded = false; +const plugins: any[] = []; +let pluginsLoaded = false; - static async getPlugins() { - if (!PluginsProvider.#pluginsLoaded) { - PluginsProvider.#pluginsLoaded = true; +async function getPlugins() { + if (!pluginsLoaded) { + pluginsLoaded = true; try { const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); const res = fs.readdirSync(absoluteFolderPath); for (const pluginFolder of res) { // @ts-ignore - PluginsProvider.#plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); + plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); } } catch (e) { console.log('error: '); @@ -70,6 +69,5 @@ class PluginsProvider { } } - return PluginsProvider.#plugins; - } + return plugins; } From 0dcb9d6b88d1e82e0abc132f566d00a6cede7322 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 18 Feb 2026 15:07:22 -0500 Subject: [PATCH 38/65] update code structure --- .github/actions/find/src/findForUrl.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index e116933..f0f4d17 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -53,21 +53,21 @@ const plugins: any[] = []; let pluginsLoaded = false; async function getPlugins() { - if (!pluginsLoaded) { - pluginsLoaded = true; - try { - const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); + if (!pluginsLoaded) { + pluginsLoaded = true; + try { + const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); - const res = fs.readdirSync(absoluteFolderPath); - for (const pluginFolder of res) { - // @ts-ignore - plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); - } - } catch (e) { - console.log('error: '); - console.log(e); + const res = fs.readdirSync(absoluteFolderPath); + for (const pluginFolder of res) { + // @ts-ignore + plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); } + } catch (e) { + console.log('error: '); + console.log(e); } + } - return plugins; + return plugins; } From 72a9ea87c310e1699b4e4b34eeceb94835aca488 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:04:25 -0500 Subject: [PATCH 39/65] test reading files for custom plugins --- .github/actions/find/src/findForUrl.ts | 51 +++++++++++++++++++------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index f0f4d17..4bccd70 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -23,7 +23,7 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis findings.push(findingData); }; - const plugins = await getPlugins(); + const plugins = await loadPlugins(); for (const plugin of plugins) { console.log('running plugin: ', plugin.name); await plugin.default({ page, addFinding, url }); @@ -52,22 +52,47 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis const plugins: any[] = []; let pluginsLoaded = false; -async function getPlugins() { +async function loadPlugins() { if (!pluginsLoaded) { pluginsLoaded = true; - try { - const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); + await loadBuiltInPlugins(); + await loadCustomPlugins(); + } + + return plugins; +} + +async function loadBuiltInPlugins() { + try { + const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); - const res = fs.readdirSync(absoluteFolderPath); - for (const pluginFolder of res) { - // @ts-ignore - plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); - } - } catch (e) { - console.log('error: '); - console.log(e); + const res = fs.readdirSync(absoluteFolderPath); + for (const pluginFolder of res) { + // @ts-ignore + plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); } + } catch (e) { + console.log('error: '); + console.log(e); } +} - return plugins; +async function loadCustomPlugins() { + console.log('cwd: ', process.cwd()); + console.log('cwd: ', process.cwd() + '/.github'); + console.log('cwd: ', process.cwd() + '/.github/scanner-plugins'); + + + // try { + // const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); + + // const res = fs.readdirSync(absoluteFolderPath); + // for (const pluginFolder of res) { + // // @ts-ignore + // plugins.push(await import('/home/runner/work/scanner-plugins/' + pluginFolder + '/index.js')); + // } + // } catch (e) { + // console.log('error: '); + // console.log(e); + // } } From 426734d6e6e7c5f08622b02e74fb0709a72ec9a2 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:07:38 -0500 Subject: [PATCH 40/65] test reading files for custom plugins --- .github/actions/find/src/findForUrl.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 4bccd70..1bfd19d 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -77,10 +77,19 @@ async function loadBuiltInPlugins() { } } +function logDirs(path: string) { + const dir = fs.readdirSync(path); + for (const folder of dir) { + if (folder !== 'node_modules') { + console.log('folder: ', folder); + } + } +} + async function loadCustomPlugins() { - console.log('cwd: ', process.cwd()); - console.log('cwd: ', process.cwd() + '/.github'); - console.log('cwd: ', process.cwd() + '/.github/scanner-plugins'); + logDirs(process.cwd()); + logDirs(process.cwd() + '/.github'); + logDirs(process.cwd() + '/.github/scanner-plugins'); // try { From dd97ecbe08b84c322780d7b245d1a941a965242b Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:09:47 -0500 Subject: [PATCH 41/65] test reading files for custom plugins --- .github/actions/find/src/findForUrl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 1bfd19d..78b13bf 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -79,6 +79,7 @@ async function loadBuiltInPlugins() { function logDirs(path: string) { const dir = fs.readdirSync(path); + console.log('path: ', path); for (const folder of dir) { if (folder !== 'node_modules') { console.log('folder: ', folder); From 538185ba9e63d67cc353ff99894bc5ae3adaf621 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:17:02 -0500 Subject: [PATCH 42/65] test with custom plugins --- .github/actions/find/src/findForUrl.ts | 27 ++++++++------------ .github/scanner-plugins/reflow-test/index.js | 4 +-- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 78b13bf..7f98447 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -88,21 +88,14 @@ function logDirs(path: string) { } async function loadCustomPlugins() { - logDirs(process.cwd()); - logDirs(process.cwd() + '/.github'); - logDirs(process.cwd() + '/.github/scanner-plugins'); - - - // try { - // const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); - - // const res = fs.readdirSync(absoluteFolderPath); - // for (const pluginFolder of res) { - // // @ts-ignore - // plugins.push(await import('/home/runner/work/scanner-plugins/' + pluginFolder + '/index.js')); - // } - // } catch (e) { - // console.log('error: '); - // console.log(e); - // } + try { + const res = fs.readdirSync(process.cwd() + '/.github/scanner-plugins'); + for (const pluginFolder of res) { + // @ts-ignore + plugins.push(await import(process.cwd() + '/.github/scanner-plugins/' + pluginFolder + '/index.js')); + } + } catch (e) { + console.log('error: '); + console.log(e); + } } diff --git a/.github/scanner-plugins/reflow-test/index.js b/.github/scanner-plugins/reflow-test/index.js index 67051f0..0b1e887 100644 --- a/.github/scanner-plugins/reflow-test/index.js +++ b/.github/scanner-plugins/reflow-test/index.js @@ -1,5 +1,5 @@ export default async function test({ page, addFinding, url } = {}) { - console.log('reflow test'); + console.log('reflow test built-in'); // Check for horizontal scrolling at 320x256 viewport try { await page.setViewportSize({ width: 320, height: 256 }); @@ -29,4 +29,4 @@ export default async function test({ page, addFinding, url } = {}) { } -export const name = 'reflow test'; +export const name = 'reflow-test'; From b89f21cd893755c544f535e2f50f7c9228701473 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:21:08 -0500 Subject: [PATCH 43/65] test with custom plugins --- .github/actions/find/src/findForUrl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 7f98447..6026096 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -64,6 +64,7 @@ async function loadPlugins() { async function loadBuiltInPlugins() { try { + console.log('Loading built-in plugins'); const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); const res = fs.readdirSync(absoluteFolderPath); @@ -89,10 +90,13 @@ function logDirs(path: string) { async function loadCustomPlugins() { try { + console.log('Loading custom plugins'); + const res = fs.readdirSync(process.cwd() + '/.github/scanner-plugins'); for (const pluginFolder of res) { // @ts-ignore - plugins.push(await import(process.cwd() + '/.github/scanner-plugins/' + pluginFolder + '/index.js')); + // plugins.push(await import(process.cwd() + '/.github/scanner-plugins/' + pluginFolder + '/index.js')); + plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); } } catch (e) { console.log('error: '); From 640b5722aed07f5f04fa93922469f8e220652f5b Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:26:43 -0500 Subject: [PATCH 44/65] test with custom plugins --- .github/actions/find/src/findForUrl.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 6026096..27a896d 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -91,13 +91,14 @@ function logDirs(path: string) { async function loadCustomPlugins() { try { console.log('Loading custom plugins'); + logDirs(process.cwd() + '/.github/scanner-plugins/reflow-test'); - const res = fs.readdirSync(process.cwd() + '/.github/scanner-plugins'); - for (const pluginFolder of res) { - // @ts-ignore - // plugins.push(await import(process.cwd() + '/.github/scanner-plugins/' + pluginFolder + '/index.js')); - plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); - } + // const res = fs.readdirSync(process.cwd() + '/.github/scanner-plugins'); + // for (const pluginFolder of res) { + // // @ts-ignore + // // plugins.push(await import(process.cwd() + '/.github/scanner-plugins/' + pluginFolder + '/index.js')); + // plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); + // } } catch (e) { console.log('error: '); console.log(e); From 991fff4690ea7aa7b48c80fd97a7b79c744d191f Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:30:18 -0500 Subject: [PATCH 45/65] test with custom plugins --- .github/actions/find/src/findForUrl.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 27a896d..a8a816d 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -91,14 +91,13 @@ function logDirs(path: string) { async function loadCustomPlugins() { try { console.log('Loading custom plugins'); - logDirs(process.cwd() + '/.github/scanner-plugins/reflow-test'); - // const res = fs.readdirSync(process.cwd() + '/.github/scanner-plugins'); - // for (const pluginFolder of res) { - // // @ts-ignore - // // plugins.push(await import(process.cwd() + '/.github/scanner-plugins/' + pluginFolder + '/index.js')); - // plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); - // } + const res = fs.readdirSync(process.cwd() + '/.github/scanner-plugins'); + for (const pluginFolder of res) { + // @ts-ignore + plugins.push(await import(process.cwd() + '/.github/scanner-plugins/' + pluginFolder + '/index.js')); + // plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); + } } catch (e) { console.log('error: '); console.log(e); From beffd516abe4f099c5b65aec6f854b97435a99f2 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:42:38 -0500 Subject: [PATCH 46/65] testing after refactor --- .github/actions/find/src/findForUrl.ts | 44 +++++++++++--------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index a8a816d..f15e2d1 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -63,40 +63,32 @@ async function loadPlugins() { } async function loadBuiltInPlugins() { - try { - console.log('Loading built-in plugins'); - const absoluteFolderPath = path.join(__dirname, '../../../scanner-plugins'); + console.log('Loading built-in plugins'); - const res = fs.readdirSync(absoluteFolderPath); - for (const pluginFolder of res) { - // @ts-ignore - plugins.push(await import('../../../scanner-plugins/' + pluginFolder + '/index.js')); - } - } catch (e) { - console.log('error: '); - console.log(e); - } + const pluginsPath = '../../../scanner-plugins/'; + loadPluginsFromPath({ + readPath: path.join(__dirname, pluginsPath), + importPath: pluginsPath, + }); } -function logDirs(path: string) { - const dir = fs.readdirSync(path); - console.log('path: ', path); - for (const folder of dir) { - if (folder !== 'node_modules') { - console.log('folder: ', folder); - } - } +async function loadCustomPlugins() { + console.log('Loading custom plugins'); + + const pluginsPath = process.cwd() + '/.github/scanner-plugins'; + loadPluginsFromPath({ + readPath: pluginsPath, + importPath: pluginsPath + }); } -async function loadCustomPlugins() { +async function loadPluginsFromPath({ readPath, importPath }: { readPath: string, importPath: string }) { try { - console.log('Loading custom plugins'); - - const res = fs.readdirSync(process.cwd() + '/.github/scanner-plugins'); + const res = fs.readdirSync(readPath); for (const pluginFolder of res) { + console.log('Found plugin: ', pluginFolder); // @ts-ignore - plugins.push(await import(process.cwd() + '/.github/scanner-plugins/' + pluginFolder + '/index.js')); - // plugins.push(await import('/home/runner/work/accessibility-sandbox/accessibility-sandbox/.github/scanner-plugins/' + pluginFolder + '/index.js')); + plugins.push(await import(importPath + pluginFolder + '/index.js')); } } catch (e) { console.log('error: '); From 3668d9575ceee20f7d236ca8066a891bae49110a Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:44:48 -0500 Subject: [PATCH 47/65] testing after refactor --- .github/actions/find/src/findForUrl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index f15e2d1..9e805b3 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -75,7 +75,7 @@ async function loadBuiltInPlugins() { async function loadCustomPlugins() { console.log('Loading custom plugins'); - const pluginsPath = process.cwd() + '/.github/scanner-plugins'; + const pluginsPath = process.cwd() + '/.github/scanner-plugins/'; loadPluginsFromPath({ readPath: pluginsPath, importPath: pluginsPath @@ -88,7 +88,7 @@ async function loadPluginsFromPath({ readPath, importPath }: { readPath: string, for (const pluginFolder of res) { console.log('Found plugin: ', pluginFolder); // @ts-ignore - plugins.push(await import(importPath + pluginFolder + '/index.js')); + plugins.push(await import(path.join(importPath, pluginFolder, '/index.js'))); } } catch (e) { console.log('error: '); From 05298383565b3552fc62083cdbfd20356c458890 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:47:57 -0500 Subject: [PATCH 48/65] testing after refactor --- .github/actions/find/src/findForUrl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 9e805b3..f82ffb0 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -66,7 +66,7 @@ async function loadBuiltInPlugins() { console.log('Loading built-in plugins'); const pluginsPath = '../../../scanner-plugins/'; - loadPluginsFromPath({ + await loadPluginsFromPath({ readPath: path.join(__dirname, pluginsPath), importPath: pluginsPath, }); @@ -76,7 +76,7 @@ async function loadCustomPlugins() { console.log('Loading custom plugins'); const pluginsPath = process.cwd() + '/.github/scanner-plugins/'; - loadPluginsFromPath({ + await loadPluginsFromPath({ readPath: pluginsPath, importPath: pluginsPath }); From 98b2037fef62378c6a41585f327e9f3e9afaf51d Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 10:55:32 -0500 Subject: [PATCH 49/65] move pluginManager to dedicated file to not bloat the 'find' file --- .github/actions/find/src/findForUrl.ts | 57 +---------------------- .github/actions/find/src/pluginManager.ts | 55 ++++++++++++++++++++++ 2 files changed, 57 insertions(+), 55 deletions(-) create mode 100644 .github/actions/find/src/pluginManager.ts diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index f82ffb0..7918b35 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -2,13 +2,7 @@ import type { Finding } from './types.d.js'; import AxeBuilder from '@axe-core/playwright' import playwright from 'playwright'; import { AuthContext } from './AuthContext.js'; -import * as fs from 'fs'; -import * as path from 'path'; -import { fileURLToPath } from 'url'; - -// Helper to get __dirname equivalent in ES Modules -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import { loadPlugins } from './pluginManager.js'; export async function findForUrl(url: string, authContext?: AuthContext): Promise { const browser = await playwright.chromium.launch({ headless: true, executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined }); @@ -25,7 +19,7 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis const plugins = await loadPlugins(); for (const plugin of plugins) { - console.log('running plugin: ', plugin.name); + console.log('Running plugin: ', plugin.name); await plugin.default({ page, addFinding, url }); } @@ -48,50 +42,3 @@ export async function findForUrl(url: string, authContext?: AuthContext): Promis await browser.close(); return findings; } - -const plugins: any[] = []; -let pluginsLoaded = false; - -async function loadPlugins() { - if (!pluginsLoaded) { - pluginsLoaded = true; - await loadBuiltInPlugins(); - await loadCustomPlugins(); - } - - return plugins; -} - -async function loadBuiltInPlugins() { - console.log('Loading built-in plugins'); - - const pluginsPath = '../../../scanner-plugins/'; - await loadPluginsFromPath({ - readPath: path.join(__dirname, pluginsPath), - importPath: pluginsPath, - }); -} - -async function loadCustomPlugins() { - console.log('Loading custom plugins'); - - const pluginsPath = process.cwd() + '/.github/scanner-plugins/'; - await loadPluginsFromPath({ - readPath: pluginsPath, - importPath: pluginsPath - }); -} - -async function loadPluginsFromPath({ readPath, importPath }: { readPath: string, importPath: string }) { - try { - const res = fs.readdirSync(readPath); - for (const pluginFolder of res) { - console.log('Found plugin: ', pluginFolder); - // @ts-ignore - plugins.push(await import(path.join(importPath, pluginFolder, '/index.js'))); - } - } catch (e) { - console.log('error: '); - console.log(e); - } -} diff --git a/.github/actions/find/src/pluginManager.ts b/.github/actions/find/src/pluginManager.ts new file mode 100644 index 0000000..7e77078 --- /dev/null +++ b/.github/actions/find/src/pluginManager.ts @@ -0,0 +1,55 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { fileURLToPath } from 'url'; + +// Helper to get __dirname equivalent in ES Modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const plugins: any[] = []; +let pluginsLoaded = false; + + +export async function loadPlugins() { + if (!pluginsLoaded) { + pluginsLoaded = true; + await loadBuiltInPlugins(); + await loadCustomPlugins(); + } + + return plugins; +} + +async function loadBuiltInPlugins() { + console.log('Loading built-in plugins'); + + const pluginsPath = '../../../scanner-plugins/'; + await loadPluginsFromPath({ + readPath: path.join(__dirname, pluginsPath), + importPath: pluginsPath, + }); +} + +async function loadCustomPlugins() { + console.log('Loading custom plugins'); + + const pluginsPath = process.cwd() + '/.github/scanner-plugins/'; + await loadPluginsFromPath({ + readPath: pluginsPath, + importPath: pluginsPath + }); +} + +async function loadPluginsFromPath({ readPath, importPath }: { readPath: string, importPath: string }) { + try { + const res = fs.readdirSync(readPath); + for (const pluginFolder of res) { + console.log('Found plugin: ', pluginFolder); + // @ts-ignore + plugins.push(await import(path.join(importPath, pluginFolder, '/index.js'))); + } + } catch (e) { + console.log('error: '); + console.log(e); + } +} From c2de456e388a589a604c6ae85cf58ea2791911a7 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 11:36:45 -0500 Subject: [PATCH 50/65] update messaging on plugin abort - add package.json to plugin to help with ESM --- .github/actions/find/src/pluginManager.ts | 20 ++++++++++++++----- .../scanner-plugins/reflow-test/package.json | 6 ++++++ 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 .github/scanner-plugins/reflow-test/package.json diff --git a/.github/actions/find/src/pluginManager.ts b/.github/actions/find/src/pluginManager.ts index 7e77078..3c58d52 100644 --- a/.github/actions/find/src/pluginManager.ts +++ b/.github/actions/find/src/pluginManager.ts @@ -11,13 +11,20 @@ let pluginsLoaded = false; export async function loadPlugins() { - if (!pluginsLoaded) { + try { + if (!pluginsLoaded) { + await loadBuiltInPlugins(); + await loadCustomPlugins(); + } + } catch (e) { + plugins.length = 0; + console.log('There was an error while loading plugins.'); + console.log('Clearing all plugins and aborting custom plugin scans.'); + console.log('Please check the logs for hints as to what may have gone wrong.'); + } finally { pluginsLoaded = true; - await loadBuiltInPlugins(); - await loadCustomPlugins(); + return plugins; } - - return plugins; } async function loadBuiltInPlugins() { @@ -49,7 +56,10 @@ async function loadPluginsFromPath({ readPath, importPath }: { readPath: string, plugins.push(await import(path.join(importPath, pluginFolder, '/index.js'))); } } catch (e) { + // - log errors here for granular info console.log('error: '); console.log(e); + // - throw error to handle aborting the plugin scans + throw(e); } } diff --git a/.github/scanner-plugins/reflow-test/package.json b/.github/scanner-plugins/reflow-test/package.json new file mode 100644 index 0000000..5bc2b0c --- /dev/null +++ b/.github/scanner-plugins/reflow-test/package.json @@ -0,0 +1,6 @@ +{ + "name": "reflow-plugin-test", + "version": "1.0.0", + "description": "A test plugin for reflow testing", + "type": "module" +} From 321c6235bbbbd1bb481b253e33f678698bf12cff Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 14:39:53 -0500 Subject: [PATCH 51/65] add plugin manager test --- .../actions/file/tests/pluginManager.test.ts | 32 -------- .github/actions/find/src/dynamicImport.ts | 10 +++ .github/actions/find/src/findForUrl.ts | 14 ++-- .github/actions/find/src/pluginManager.ts | 78 ++++++++++++------- .../actions/find/tests/pluginManager.test.ts | 57 ++++++++++++++ 5 files changed, 122 insertions(+), 69 deletions(-) delete mode 100644 .github/actions/file/tests/pluginManager.test.ts create mode 100644 .github/actions/find/src/dynamicImport.ts create mode 100644 .github/actions/find/tests/pluginManager.test.ts diff --git a/.github/actions/file/tests/pluginManager.test.ts b/.github/actions/file/tests/pluginManager.test.ts deleted file mode 100644 index d32ac5e..0000000 --- a/.github/actions/file/tests/pluginManager.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {describe, it, expect, vi} from 'vitest' -import * as pluginManager from '../src/pluginManager.js' -import * as fs from 'fs' - - -// Mock generateIssueBody so we can inspect what screenshotRepo is passed -// vi.mock('../src/pluginManager.js', () => ({ -// loadPlugins, -// loadBuiltInPlugins: vi.fn(() => Promise.resolve()), -// loadCustomPlugins: vi.fn(() => Promise.resolve()) -// })) - -describe('loadPlugins', () => { - vi.spyOn(pluginManager, 'importWrapper').mockImplementation(() => '') - - describe('when plugins are not loaded', () => { - vi.spyOn(fs, 'readdirSync').mockImplementation(() => ['plugin-1', 'plugin-2']) - - it('loads them', async () => { - const plugins = await pluginManager.loadPlugins() - expect(pluginManager.importWrapper).toHaveBeenCalledTimes(2) - expect(plugins.length).toBe(2) - }) - }) - - // describe('when plugins are loaded', () => { - // it('theyre cached and not loaded again', async () => { - - // }) - // }) - -}) diff --git a/.github/actions/find/src/dynamicImport.ts b/.github/actions/find/src/dynamicImport.ts new file mode 100644 index 0000000..e3cbce9 --- /dev/null +++ b/.github/actions/find/src/dynamicImport.ts @@ -0,0 +1,10 @@ + +// - this exists because I'm not sure how to mock +// the dynamic import function, so mocking this instead +// - also, vitest has a limitation on mocking: +// https://vitest.dev/guide/mocking/modules.html#mocking-modules-pitfalls +// - basically if a function is called by another function in the same file +// it can't be mocked. So this was extracted into a separate file +export async function dynamicImport(path: string) { + return import(path) +} diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 64e5159..1e7784e 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -3,7 +3,7 @@ import AxeBuilder from '@axe-core/playwright' import playwright from 'playwright' import {AuthContext} from './AuthContext.js' import {generateScreenshots} from './generateScreenshots.js' -import { loadPlugins } from './pluginManager.js'; +import { loadPlugins } from './pluginManager.js' export async function findForUrl( @@ -21,18 +21,18 @@ export async function findForUrl( await page.goto(url) console.log(`Scanning ${page.url()}`) - let findings: Finding[] = []; + let findings: Finding[] = [] const addFinding = (findingData: Finding) => { - findings.push(findingData); - }; + findings.push(findingData) + } try { const rawFindings = await new AxeBuilder({page}).analyze() - const plugins = await loadPlugins(); + const plugins = await loadPlugins() for (const plugin of plugins) { - console.log('Running plugin: ', plugin.name); - await plugin.default({ page, addFinding, url }); + console.log('Running plugin: ', plugin.name) + await plugin.default({ page, addFinding, url }) } let screenshotId: string | undefined diff --git a/.github/actions/find/src/pluginManager.ts b/.github/actions/find/src/pluginManager.ts index 3c58d52..afe3a3c 100644 --- a/.github/actions/find/src/pluginManager.ts +++ b/.github/actions/find/src/pluginManager.ts @@ -1,65 +1,83 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { fileURLToPath } from 'url'; +import * as fs from 'fs' +import * as path from 'path' +import { fileURLToPath } from 'url' +import { dynamicImport } from './dynamicImport.js' // Helper to get __dirname equivalent in ES Modules -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) -const plugins: any[] = []; -let pluginsLoaded = false; +const plugins: any[] = [] +let pluginsLoaded = false export async function loadPlugins() { + console.log('loading plugins') + try { if (!pluginsLoaded) { - await loadBuiltInPlugins(); - await loadCustomPlugins(); + await loadBuiltInPlugins() + await loadCustomPlugins() } } catch (e) { - plugins.length = 0; - console.log('There was an error while loading plugins.'); - console.log('Clearing all plugins and aborting custom plugin scans.'); - console.log('Please check the logs for hints as to what may have gone wrong.'); + plugins.length = 0 + console.log(abortError) } finally { - pluginsLoaded = true; - return plugins; + pluginsLoaded = true + return plugins } } -async function loadBuiltInPlugins() { - console.log('Loading built-in plugins'); +export const abortError = [ + 'There was an error while loading plugins.', + 'Clearing all plugins and aborting custom plugin scans.', + 'Please check the logs for hints as to what may have gone wrong.' +].join('\n') + + +export function clearCache() { + console.log('clearing plugin cache') + pluginsLoaded = false + plugins.length = 0 +} + + +// exported for mocking/testing. not for actual use +export async function loadBuiltInPlugins() { + console.log('Loading built-in plugins') - const pluginsPath = '../../../scanner-plugins/'; + const pluginsPath = '../../../scanner-plugins/' await loadPluginsFromPath({ readPath: path.join(__dirname, pluginsPath), importPath: pluginsPath, - }); + }) } -async function loadCustomPlugins() { - console.log('Loading custom plugins'); +// exported for mocking/testing. not for actual use +export async function loadCustomPlugins() { + console.log('Loading custom plugins') - const pluginsPath = process.cwd() + '/.github/scanner-plugins/'; + const pluginsPath = process.cwd() + '/.github/scanner-plugins/' await loadPluginsFromPath({ readPath: pluginsPath, importPath: pluginsPath - }); + }) } -async function loadPluginsFromPath({ readPath, importPath }: { readPath: string, importPath: string }) { +// exported for mocking/testing. not for actual use +export async function loadPluginsFromPath({ readPath, importPath }: { readPath: string, importPath: string }) { try { - const res = fs.readdirSync(readPath); + const res = fs.readdirSync(readPath) for (const pluginFolder of res) { - console.log('Found plugin: ', pluginFolder); + console.log('Found plugin: ', pluginFolder) // @ts-ignore - plugins.push(await import(path.join(importPath, pluginFolder, '/index.js'))); + plugins.push(await dynamicImport(path.join(importPath, pluginFolder, '/index.js'))) } } catch (e) { // - log errors here for granular info - console.log('error: '); - console.log(e); + console.log('error: ') + console.log(e) // - throw error to handle aborting the plugin scans - throw(e); + throw(e) } } diff --git a/.github/actions/find/tests/pluginManager.test.ts b/.github/actions/find/tests/pluginManager.test.ts new file mode 100644 index 0000000..a185ca5 --- /dev/null +++ b/.github/actions/find/tests/pluginManager.test.ts @@ -0,0 +1,57 @@ +import {describe, it, expect, vi, beforeEach} from 'vitest' + +import * as fs from 'fs' +import * as dynamicImportModule from '../src/dynamicImport.js' +import * as pluginManager from '../src/pluginManager.js' + + +// - enable spying on fs +// https://vitest.dev/guide/browser/#limitations +vi.mock('fs', { spy: true }) +vi.mock('../src/pluginManager.js', { spy: true }) + +describe('loadPlugins', () => { + vi.spyOn(dynamicImportModule, 'dynamicImport').mockImplementation((path) => ( + Promise.resolve(path) + )) + beforeEach(() => { + vi.spyOn(fs, 'readdirSync').mockImplementation((readPath) => { + return [readPath + '/plugin-1', readPath + '/plugin-2'] + }) + }) + + describe('when plugins are not loaded', () => { + it('loads them', async () => { + pluginManager.clearCache() + const plugins = await pluginManager.loadPlugins() + expect(dynamicImportModule.dynamicImport).toHaveBeenCalledTimes(4) + expect(plugins.length).toBe(4) + }) + }) + + describe('when plugins are already loaded', () => { + it('caches them and doesnt load them again', async () => { + pluginManager.clearCache() + await pluginManager.loadPlugins() + await pluginManager.loadPlugins() + expect(pluginManager.loadBuiltInPlugins).toHaveBeenCalledTimes(0) + expect(pluginManager.loadCustomPlugins).toHaveBeenCalledTimes(0) + }) + }) + + describe('when there is an error loading plugins', () => { + beforeEach(() => { + vi.spyOn(fs, 'readdirSync').mockImplementation(() => { + throw new Error('test error') + }) + }) + + it('Aborts loading all plugins', async () => { + pluginManager.clearCache() + const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) + const plugins = await pluginManager.loadPlugins() + expect(plugins.length).toBe(0) + expect(consoleLogSpy).toHaveBeenCalledWith(pluginManager.abortError) + }) + }) +}) From 0b87aaff55bb77914c120f0f6e12690c81deb735 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Mon, 23 Feb 2026 15:02:00 -0500 Subject: [PATCH 52/65] eslint cleanup --- .github/actions/find/src/pluginManager.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/pluginManager.ts b/.github/actions/find/src/pluginManager.ts index afe3a3c..43cd69d 100644 --- a/.github/actions/find/src/pluginManager.ts +++ b/.github/actions/find/src/pluginManager.ts @@ -7,6 +7,8 @@ import { dynamicImport } from './dynamicImport.js' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) +// - plugins are js files right now, so they dont have a type +// eslint-disable-next-line @typescript-eslint/no-explicit-any const plugins: any[] = [] let pluginsLoaded = false @@ -19,7 +21,7 @@ export async function loadPlugins() { await loadBuiltInPlugins() await loadCustomPlugins() } - } catch (e) { + } catch { plugins.length = 0 console.log(abortError) } finally { @@ -70,7 +72,6 @@ export async function loadPluginsFromPath({ readPath, importPath }: { readPath: const res = fs.readdirSync(readPath) for (const pluginFolder of res) { console.log('Found plugin: ', pluginFolder) - // @ts-ignore plugins.push(await dynamicImport(path.join(importPath, pluginFolder, '/index.js'))) } } catch (e) { From 9a27f85d58ded8660276b9fa2545aade6b5a95cb Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 09:31:09 -0500 Subject: [PATCH 53/65] fix formatting --- .github/actions/find/src/dynamicImport.ts | 1 - .github/actions/find/src/findForUrl.ts | 5 ++--- .github/actions/find/src/pluginManager.ts | 15 ++++++--------- .github/actions/find/tests/pluginManager.test.ts | 13 ++++++------- .vscode/settings.json | 1 + 5 files changed, 15 insertions(+), 20 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.github/actions/find/src/dynamicImport.ts b/.github/actions/find/src/dynamicImport.ts index e3cbce9..caba867 100644 --- a/.github/actions/find/src/dynamicImport.ts +++ b/.github/actions/find/src/dynamicImport.ts @@ -1,4 +1,3 @@ - // - this exists because I'm not sure how to mock // the dynamic import function, so mocking this instead // - also, vitest has a limitation on mocking: diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 1e7784e..a968f43 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -3,8 +3,7 @@ import AxeBuilder from '@axe-core/playwright' import playwright from 'playwright' import {AuthContext} from './AuthContext.js' import {generateScreenshots} from './generateScreenshots.js' -import { loadPlugins } from './pluginManager.js' - +import {loadPlugins} from './pluginManager.js' export async function findForUrl( url: string, @@ -32,7 +31,7 @@ export async function findForUrl( const plugins = await loadPlugins() for (const plugin of plugins) { console.log('Running plugin: ', plugin.name) - await plugin.default({ page, addFinding, url }) + await plugin.default({page, addFinding, url}) } let screenshotId: string | undefined diff --git a/.github/actions/find/src/pluginManager.ts b/.github/actions/find/src/pluginManager.ts index 43cd69d..9e02282 100644 --- a/.github/actions/find/src/pluginManager.ts +++ b/.github/actions/find/src/pluginManager.ts @@ -1,7 +1,7 @@ import * as fs from 'fs' import * as path from 'path' -import { fileURLToPath } from 'url' -import { dynamicImport } from './dynamicImport.js' +import {fileURLToPath} from 'url' +import {dynamicImport} from './dynamicImport.js' // Helper to get __dirname equivalent in ES Modules const __filename = fileURLToPath(import.meta.url) @@ -12,7 +12,6 @@ const __dirname = path.dirname(__filename) const plugins: any[] = [] let pluginsLoaded = false - export async function loadPlugins() { console.log('loading plugins') @@ -33,17 +32,15 @@ export async function loadPlugins() { export const abortError = [ 'There was an error while loading plugins.', 'Clearing all plugins and aborting custom plugin scans.', - 'Please check the logs for hints as to what may have gone wrong.' + 'Please check the logs for hints as to what may have gone wrong.', ].join('\n') - export function clearCache() { console.log('clearing plugin cache') pluginsLoaded = false plugins.length = 0 } - // exported for mocking/testing. not for actual use export async function loadBuiltInPlugins() { console.log('Loading built-in plugins') @@ -62,12 +59,12 @@ export async function loadCustomPlugins() { const pluginsPath = process.cwd() + '/.github/scanner-plugins/' await loadPluginsFromPath({ readPath: pluginsPath, - importPath: pluginsPath + importPath: pluginsPath, }) } // exported for mocking/testing. not for actual use -export async function loadPluginsFromPath({ readPath, importPath }: { readPath: string, importPath: string }) { +export async function loadPluginsFromPath({readPath, importPath}: {readPath: string; importPath: string}) { try { const res = fs.readdirSync(readPath) for (const pluginFolder of res) { @@ -79,6 +76,6 @@ export async function loadPluginsFromPath({ readPath, importPath }: { readPath: console.log('error: ') console.log(e) // - throw error to handle aborting the plugin scans - throw(e) + throw e } } diff --git a/.github/actions/find/tests/pluginManager.test.ts b/.github/actions/find/tests/pluginManager.test.ts index a185ca5..70eff18 100644 --- a/.github/actions/find/tests/pluginManager.test.ts +++ b/.github/actions/find/tests/pluginManager.test.ts @@ -4,18 +4,17 @@ import * as fs from 'fs' import * as dynamicImportModule from '../src/dynamicImport.js' import * as pluginManager from '../src/pluginManager.js' - // - enable spying on fs // https://vitest.dev/guide/browser/#limitations -vi.mock('fs', { spy: true }) -vi.mock('../src/pluginManager.js', { spy: true }) +vi.mock('fs', {spy: true}) +vi.mock('../src/pluginManager.js', {spy: true}) describe('loadPlugins', () => { - vi.spyOn(dynamicImportModule, 'dynamicImport').mockImplementation((path) => ( - Promise.resolve(path) - )) + vi.spyOn(dynamicImportModule, 'dynamicImport').mockImplementation(path => Promise.resolve(path)) beforeEach(() => { - vi.spyOn(fs, 'readdirSync').mockImplementation((readPath) => { + // @ts-expect-error - we don't need the full fs readdirsync + // method signature here + vi.spyOn(fs, 'readdirSync').mockImplementation(readPath => { return [readPath + '/plugin-1', readPath + '/plugin-2'] }) }) diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} From 1d2753ab3dcde0a8a516584a82098e62f814283d Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 10:01:48 -0500 Subject: [PATCH 54/65] read scans input --- .github/actions/find/src/findForUrl.ts | 20 ++++++++++++--- .../actions/find/src/scansContextProvider.ts | 25 +++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 .github/actions/find/src/scansContextProvider.ts diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index a968f43..4c83033 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -4,6 +4,8 @@ import playwright from 'playwright' import {AuthContext} from './AuthContext.js' import {generateScreenshots} from './generateScreenshots.js' import {loadPlugins} from './pluginManager.js' +import {getScansContext} from './scansContextProvider.js' +import axe from 'axe-core' export async function findForUrl( url: string, @@ -26,12 +28,21 @@ export async function findForUrl( } try { - const rawFindings = await new AxeBuilder({page}).analyze() + const scansContext = getScansContext() + + let rawFindings = {} as axe.AxeResults + if (scansContext.shouldPerformAxeScan) { + rawFindings = await new AxeBuilder({page}).analyze() + } const plugins = await loadPlugins() for (const plugin of plugins) { - console.log('Running plugin: ', plugin.name) - await plugin.default({page, addFinding, url}) + if (scansContext.scans.includes(plugin.name)) { + console.log('Running plugin: ', plugin.name) + await plugin.default({page, addFinding, url}) + } else { + console.log(`Skipping plugin ${plugin.name} because it is not included in the 'scans' input`) + } } let screenshotId: string | undefined @@ -39,7 +50,8 @@ export async function findForUrl( screenshotId = await generateScreenshots(page) } - findings = rawFindings.violations.map(violation => ({ + console.log('rawFindings: ', rawFindings) + findings = rawFindings?.violations.map(violation => ({ scannerType: 'axe', url, html: violation.nodes[0].html.replace(/'/g, '''), diff --git a/.github/actions/find/src/scansContextProvider.ts b/.github/actions/find/src/scansContextProvider.ts new file mode 100644 index 0000000..d68415e --- /dev/null +++ b/.github/actions/find/src/scansContextProvider.ts @@ -0,0 +1,25 @@ +import core from '@actions/core' + +type ScansContext = { + scans: Array + shouldPerformAxeScan: boolean +} +let scansContext: ScansContext | undefined + +export function getScansContext() { + if (!scansContext) { + const scansJson = core.getInput('scans', {required: false}) + const scans = JSON.parse(scansJson || '{}') + + scansContext = { + scans, + // - if no 'scans' input is provided, we default to the existing behavior + // (only axe scan) for backwards compatability. + // - we can enforce using the 'scans' input in a future major release and + // mark it as required + shouldPerformAxeScan: !scansJson || scans.includes('axe'), + } + } + + return scansContext +} From cd4771be965f9d79b6ab4ac42e3a3c828d3d75f3 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 15:23:30 -0500 Subject: [PATCH 55/65] add tests - clean up formatting and linting --- .github/actions/find/src/findForUrl.ts | 45 ++++---- .github/actions/find/src/pluginManager.ts | 12 +- .../actions/find/src/scansContextProvider.ts | 19 +++- .github/actions/find/tests/findForUrl.test.ts | 106 ++++++++++++++++++ 4 files changed, 155 insertions(+), 27 deletions(-) create mode 100644 .github/actions/find/tests/findForUrl.test.ts diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 4c83033..1db77c8 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -30,18 +30,24 @@ export async function findForUrl( try { const scansContext = getScansContext() - let rawFindings = {} as axe.AxeResults + let rawFindings: axe.AxeResults | undefined if (scansContext.shouldPerformAxeScan) { rawFindings = await new AxeBuilder({page}).analyze() } - const plugins = await loadPlugins() - for (const plugin of plugins) { - if (scansContext.scans.includes(plugin.name)) { - console.log('Running plugin: ', plugin.name) - await plugin.default({page, addFinding, url}) - } else { - console.log(`Skipping plugin ${plugin.name} because it is not included in the 'scans' input`) + // - this condition is not required, but makes it easier to make assertions + // in unit tests on whether 'loadPlugins' was called or not + // - alternatively, we can wrap the 'plugin.default(...)' call in another function + // and make assertions on whether that function was called or not + if (scansContext.shouldRunPlugins) { + const plugins = await loadPlugins() + for (const plugin of plugins) { + if (scansContext.scansToPerform.includes(plugin.name)) { + console.log('Running plugin: ', plugin.name) + await plugin.default({page, addFinding, url}) + } else { + console.log(`Skipping plugin ${plugin.name} because it is not included in the 'scans' input`) + } } } @@ -51,17 +57,18 @@ export async function findForUrl( } console.log('rawFindings: ', rawFindings) - findings = rawFindings?.violations.map(violation => ({ - scannerType: 'axe', - url, - html: violation.nodes[0].html.replace(/'/g, '''), - problemShort: violation.help.toLowerCase().replace(/'/g, '''), - problemUrl: violation.helpUrl.replace(/'/g, '''), - ruleId: violation.id, - solutionShort: violation.description.toLowerCase().replace(/'/g, '''), - solutionLong: violation.nodes[0].failureSummary?.replace(/'/g, '''), - screenshotId, - })) + findings = + rawFindings?.violations.map(violation => ({ + scannerType: 'axe', + url, + html: violation.nodes[0].html.replace(/'/g, '''), + problemShort: violation.help.toLowerCase().replace(/'/g, '''), + problemUrl: violation.helpUrl.replace(/'/g, '''), + ruleId: violation.id, + solutionShort: violation.description.toLowerCase().replace(/'/g, '''), + solutionLong: violation.nodes[0].failureSummary?.replace(/'/g, '''), + screenshotId, + })) || [] } catch (e) { console.error('Error during accessibility scan:', e) } diff --git a/.github/actions/find/src/pluginManager.ts b/.github/actions/find/src/pluginManager.ts index 9e02282..46f04ab 100644 --- a/.github/actions/find/src/pluginManager.ts +++ b/.github/actions/find/src/pluginManager.ts @@ -2,14 +2,19 @@ import * as fs from 'fs' import * as path from 'path' import {fileURLToPath} from 'url' import {dynamicImport} from './dynamicImport.js' +import type {Finding} from './types.d.js' +import playwright from 'playwright' // Helper to get __dirname equivalent in ES Modules const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -// - plugins are js files right now, so they dont have a type -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const plugins: any[] = [] +export type Plugin = { + name: string + default: (options: {page: playwright.Page; addFinding: (findingData: Finding) => void; url: string}) => Promise +} + +const plugins: Plugin[] = [] let pluginsLoaded = false export async function loadPlugins() { @@ -36,7 +41,6 @@ export const abortError = [ ].join('\n') export function clearCache() { - console.log('clearing plugin cache') pluginsLoaded = false plugins.length = 0 } diff --git a/.github/actions/find/src/scansContextProvider.ts b/.github/actions/find/src/scansContextProvider.ts index d68415e..e21d512 100644 --- a/.github/actions/find/src/scansContextProvider.ts +++ b/.github/actions/find/src/scansContextProvider.ts @@ -1,25 +1,36 @@ import core from '@actions/core' type ScansContext = { - scans: Array + scansToPerform: Array shouldPerformAxeScan: boolean + shouldRunPlugins: boolean } let scansContext: ScansContext | undefined export function getScansContext() { if (!scansContext) { const scansJson = core.getInput('scans', {required: false}) - const scans = JSON.parse(scansJson || '{}') + console.log('scans input: ', scansJson) + const scansToPerform = JSON.parse(scansJson || '{}') + // - if we dont have a scans input + // or we do have a scans input, but it only has 1 item and its 'axe' + // then we only want to run 'axe' and not the plugins + const onlyAxeScan = scansToPerform.length === 0 || (scansToPerform.length === 1 && scansToPerform[0] === 'axe') scansContext = { - scans, + scansToPerform, // - if no 'scans' input is provided, we default to the existing behavior // (only axe scan) for backwards compatability. // - we can enforce using the 'scans' input in a future major release and // mark it as required - shouldPerformAxeScan: !scansJson || scans.includes('axe'), + shouldPerformAxeScan: !scansJson || scansToPerform.includes('axe'), + shouldRunPlugins: scansToPerform.length > 0 && !onlyAxeScan, } } return scansContext } + +export function clearCache() { + scansContext = undefined +} diff --git a/.github/actions/find/tests/findForUrl.test.ts b/.github/actions/find/tests/findForUrl.test.ts new file mode 100644 index 0000000..2589cd4 --- /dev/null +++ b/.github/actions/find/tests/findForUrl.test.ts @@ -0,0 +1,106 @@ +import {describe, it, expect, vi} from 'vitest' +import core from '@actions/core' +import {findForUrl} from '../src/findForUrl.js' +import AxeBuilder from '@axe-core/playwright' +import axe from 'axe-core' +import * as pluginManager from '../src/pluginManager.js' +import {clearCache} from '../src/scansContextProvider.js' + +vi.mock('playwright', () => ({ + default: { + chromium: { + launch: () => ({ + newContext: () => ({ + newPage: () => ({ + pageUrl: '', + goto: () => {}, + url: () => {}, + }), + close: () => {}, + }), + close: () => {}, + }), + }, + }, +})) + +vi.mock('@axe-core/playwright', () => { + const AxeBuilderMock = vi.fn() + AxeBuilderMock.prototype.analyze = vi.fn(() => { + console.log('calling mock analyze') + return Promise.resolve({violations: []} as unknown as axe.AxeResults) + }) + return {default: AxeBuilderMock} +}) + +let actionInput: string = '' +let loadedPlugins: pluginManager.Plugin[] = [] + +function clearAll() { + clearCache() + vi.clearAllMocks() +} + +describe('findForUrl', () => { + vi.spyOn(core, 'getInput').mockImplementation(() => actionInput) + vi.spyOn(pluginManager, 'loadPlugins').mockImplementation(() => Promise.resolve(loadedPlugins)) + + async function axeOnlyTest() { + clearAll() + + await findForUrl('test.com') + expect(AxeBuilder.prototype.analyze).toHaveBeenCalledTimes(1) + expect(pluginManager.loadPlugins).toHaveBeenCalledTimes(0) + } + + describe('when no scans list is provided', () => { + it('defaults to running only axe scan', async () => { + actionInput = '' + await axeOnlyTest() + }) + }) + + describe('when a scans list is provided', () => { + describe('and the list _only_ includes axe', () => { + it('runs only the axe scan', async () => { + actionInput = JSON.stringify(['axe']) + await axeOnlyTest() + }) + }) + + describe('and the list includes axe and other scans', () => { + it('runs axe and plugins', async () => { + actionInput = JSON.stringify(['axe', 'custom-scan']) + clearAll() + + await findForUrl('test.com') + expect(AxeBuilder.prototype.analyze).toHaveBeenCalledTimes(1) + expect(pluginManager.loadPlugins).toHaveBeenCalledTimes(1) + }) + }) + + describe('and the list does not include axe', () => { + it('only runs plugins', async () => { + actionInput = JSON.stringify(['custom-scan']) + clearAll() + + await findForUrl('test.com') + expect(AxeBuilder.prototype.analyze).toHaveBeenCalledTimes(0) + expect(pluginManager.loadPlugins).toHaveBeenCalledTimes(1) + }) + }) + + it('should only run scans that are included in the list', async () => { + loadedPlugins = [ + {name: 'custom-scan-1', default: vi.fn()}, + {name: 'custom-scan-2', default: vi.fn()}, + ] + actionInput = JSON.stringify(['custom-scan-1']) + clearAll() + + await findForUrl('test.com') + expect(loadedPlugins[0].default).toHaveBeenCalledTimes(1) + expect(loadedPlugins[1].default).toHaveBeenCalledTimes(0) + }) + }) +}) From 407bc846d44b33197355fd638004c0504c75a1b2 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 15:29:10 -0500 Subject: [PATCH 56/65] update comments --- .github/actions/find/src/findForUrl.ts | 3 ++- .github/actions/find/src/scansContextProvider.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 1db77c8..aa223b7 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -39,6 +39,8 @@ export async function findForUrl( // in unit tests on whether 'loadPlugins' was called or not // - alternatively, we can wrap the 'plugin.default(...)' call in another function // and make assertions on whether that function was called or not + // - the other option is to wrap each plugin in a class instance + // and make assertions on something like 'plugin.run' being called or not if (scansContext.shouldRunPlugins) { const plugins = await loadPlugins() for (const plugin of plugins) { @@ -56,7 +58,6 @@ export async function findForUrl( screenshotId = await generateScreenshots(page) } - console.log('rawFindings: ', rawFindings) findings = rawFindings?.violations.map(violation => ({ scannerType: 'axe', diff --git a/.github/actions/find/src/scansContextProvider.ts b/.github/actions/find/src/scansContextProvider.ts index e21d512..a1660c8 100644 --- a/.github/actions/find/src/scansContextProvider.ts +++ b/.github/actions/find/src/scansContextProvider.ts @@ -10,11 +10,11 @@ let scansContext: ScansContext | undefined export function getScansContext() { if (!scansContext) { const scansJson = core.getInput('scans', {required: false}) - console.log('scans input: ', scansJson) const scansToPerform = JSON.parse(scansJson || '{}') // - if we dont have a scans input // or we do have a scans input, but it only has 1 item and its 'axe' // then we only want to run 'axe' and not the plugins + // - keep in mind, 'onlyAxeScan' is not the same as 'shouldPerformAxeScan' const onlyAxeScan = scansToPerform.length === 0 || (scansToPerform.length === 1 && scansToPerform[0] === 'axe') scansContext = { From 2e09eb0b38f64271d7800b3ff33194dd062f7766 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 15:30:43 -0500 Subject: [PATCH 57/65] remove console.log --- .github/actions/find/tests/findForUrl.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/actions/find/tests/findForUrl.test.ts b/.github/actions/find/tests/findForUrl.test.ts index 2589cd4..5ce7682 100644 --- a/.github/actions/find/tests/findForUrl.test.ts +++ b/.github/actions/find/tests/findForUrl.test.ts @@ -26,10 +26,7 @@ vi.mock('playwright', () => ({ vi.mock('@axe-core/playwright', () => { const AxeBuilderMock = vi.fn() - AxeBuilderMock.prototype.analyze = vi.fn(() => { - console.log('calling mock analyze') - return Promise.resolve({violations: []} as unknown as axe.AxeResults) - }) + AxeBuilderMock.prototype.analyze = vi.fn(() => Promise.resolve({violations: []} as unknown as axe.AxeResults)) return {default: AxeBuilderMock} }) From a8ec788eeb4650c4ba58f9e44d3a1b0f2c275312 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 15:33:43 -0500 Subject: [PATCH 58/65] remove .vscode and add to gitignore --- .gitignore | 3 ++- .vscode/settings.json | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index f42521c..cbb4dba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ dist node_modules -test-results \ No newline at end of file +test-results +.vscode diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 0967ef4..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1 +0,0 @@ -{} From 600d158ada140f0bab1cc5f2f8a58d58a694010a Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 15:38:01 -0500 Subject: [PATCH 59/65] clean up a line to make it easier to read --- .github/actions/find/tests/findForUrl.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/find/tests/findForUrl.test.ts b/.github/actions/find/tests/findForUrl.test.ts index 5ce7682..25f4d1f 100644 --- a/.github/actions/find/tests/findForUrl.test.ts +++ b/.github/actions/find/tests/findForUrl.test.ts @@ -26,7 +26,8 @@ vi.mock('playwright', () => ({ vi.mock('@axe-core/playwright', () => { const AxeBuilderMock = vi.fn() - AxeBuilderMock.prototype.analyze = vi.fn(() => Promise.resolve({violations: []} as unknown as axe.AxeResults)) + const rawFinding = {violations: []} as unknown as axe.AxeResults + AxeBuilderMock.prototype.analyze = vi.fn(() => Promise.resolve(rawFinding)) return {default: AxeBuilderMock} }) From e86870ec4fe2006a5ab5c8339da36b3ccbc84dda Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 15:43:20 -0500 Subject: [PATCH 60/65] update action and readme --- .github/actions/find/README.md | 8 ++++++++ .github/actions/find/action.yml | 23 +++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/actions/find/README.md b/.github/actions/find/README.md index f674e23..6e0d1f6 100644 --- a/.github/actions/find/README.md +++ b/.github/actions/find/README.md @@ -19,6 +19,14 @@ https://primer.style/octicons/ **Optional** Stringified JSON object containing `username`, `password`, `cookies`, and/or `localStorage` from an authenticated session. For example: `{"username":"some-user","password":"correct-horse-battery-staple","cookies":[{"name":"theme-preference","value":"light","domain":"primer.style","path":"/"}],"localStorage":{"https://primer.style":{"theme-preference":"light"}}}` +#### `include_screenshots` + +**Optional** Bool - whether to capture screenshots of scanned pages and include links to them in the issue + +#### `Scans` + +**Optional** Stringified JSON array of scans (string) to perform. If not provided, only axe will be performed. Valid options currently include 'axe' + ### Outputs #### `findings` diff --git a/.github/actions/find/action.yml b/.github/actions/find/action.yml index 2ab8dcb..c48b7bd 100644 --- a/.github/actions/find/action.yml +++ b/.github/actions/find/action.yml @@ -1,27 +1,30 @@ -name: "Find" -description: "Finds potential accessibility gaps." +name: 'Find' +description: 'Finds potential accessibility gaps.' inputs: urls: - description: "Newline-delimited list of URLs to check for accessibility issues" + description: 'Newline-delimited list of URLs to check for accessibility issues' required: true multiline: true auth_context: description: "Stringified JSON object containing 'username', 'password', 'cookies', and/or 'localStorage' from an authenticated session" required: false include_screenshots: - description: "Whether to capture screenshots of scanned pages and include links to them in the issue" + description: 'Whether to capture screenshots of scanned pages and include links to them in the issue' + required: false + default: 'false' + scans: + description: 'Stringified JSON array of scans to perform. If not provided, only axe will be performed' required: false - default: "false" outputs: findings: - description: "List of potential accessibility gaps, as stringified JSON" + description: 'List of potential accessibility gaps, as stringified JSON' runs: - using: "node24" - main: "bootstrap.js" + using: 'node24' + main: 'bootstrap.js' branding: - icon: "compass" - color: "blue" + icon: 'compass' + color: 'blue' From 4e43b7fb6ee971d965854dd176d2f44fed25e77c Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 15:51:07 -0500 Subject: [PATCH 61/65] fix casing for 'scans' in readme --- .github/actions/find/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/find/README.md b/.github/actions/find/README.md index 6e0d1f6..4ee79e2 100644 --- a/.github/actions/find/README.md +++ b/.github/actions/find/README.md @@ -23,7 +23,7 @@ https://primer.style/octicons/ **Optional** Bool - whether to capture screenshots of scanned pages and include links to them in the issue -#### `Scans` +#### `scans` **Optional** Stringified JSON array of scans (string) to perform. If not provided, only axe will be performed. Valid options currently include 'axe' From 8402e616ac84ba249bf59d8d2966f9b203f7e6fd Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Tue, 24 Feb 2026 16:33:32 -0500 Subject: [PATCH 62/65] copilot PR feedback - update plugin manager tests to account for new isDirectory check --- .github/actions/find/src/findForUrl.ts | 26 +++++++++---------- .github/actions/find/src/pluginManager.ts | 7 +++-- .../actions/find/src/scansContextProvider.ts | 4 +-- .../actions/find/tests/pluginManager.test.ts | 5 ++++ 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index aa223b7..0af96ad 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -22,7 +22,7 @@ export async function findForUrl( await page.goto(url) console.log(`Scanning ${page.url()}`) - let findings: Finding[] = [] + const findings: Finding[] = [] const addFinding = (findingData: Finding) => { findings.push(findingData) } @@ -58,18 +58,18 @@ export async function findForUrl( screenshotId = await generateScreenshots(page) } - findings = - rawFindings?.violations.map(violation => ({ - scannerType: 'axe', - url, - html: violation.nodes[0].html.replace(/'/g, '''), - problemShort: violation.help.toLowerCase().replace(/'/g, '''), - problemUrl: violation.helpUrl.replace(/'/g, '''), - ruleId: violation.id, - solutionShort: violation.description.toLowerCase().replace(/'/g, '''), - solutionLong: violation.nodes[0].failureSummary?.replace(/'/g, '''), - screenshotId, - })) || [] + const axeFindings = rawFindings?.violations.map(violation => ({ + scannerType: 'axe', + url, + html: violation.nodes[0].html.replace(/'/g, '''), + problemShort: violation.help.toLowerCase().replace(/'/g, '''), + problemUrl: violation.helpUrl.replace(/'/g, '''), + ruleId: violation.id, + solutionShort: violation.description.toLowerCase().replace(/'/g, '''), + solutionLong: violation.nodes[0].failureSummary?.replace(/'/g, '''), + screenshotId, + })) + findings.push(...(axeFindings || [])) } catch (e) { console.error('Error during accessibility scan:', e) } diff --git a/.github/actions/find/src/pluginManager.ts b/.github/actions/find/src/pluginManager.ts index 46f04ab..6a64d80 100644 --- a/.github/actions/find/src/pluginManager.ts +++ b/.github/actions/find/src/pluginManager.ts @@ -72,8 +72,11 @@ export async function loadPluginsFromPath({readPath, importPath}: {readPath: str try { const res = fs.readdirSync(readPath) for (const pluginFolder of res) { - console.log('Found plugin: ', pluginFolder) - plugins.push(await dynamicImport(path.join(importPath, pluginFolder, '/index.js'))) + const pluginFolderPath = path.join(importPath, pluginFolder) + if (fs.lstatSync(pluginFolderPath).isDirectory()) { + console.log('Found plugin: ', pluginFolder) + plugins.push(await dynamicImport(path.join(importPath, pluginFolder, '/index.js'))) + } } } catch (e) { // - log errors here for granular info diff --git a/.github/actions/find/src/scansContextProvider.ts b/.github/actions/find/src/scansContextProvider.ts index a1660c8..2d6a2ef 100644 --- a/.github/actions/find/src/scansContextProvider.ts +++ b/.github/actions/find/src/scansContextProvider.ts @@ -10,8 +10,8 @@ let scansContext: ScansContext | undefined export function getScansContext() { if (!scansContext) { const scansJson = core.getInput('scans', {required: false}) - const scansToPerform = JSON.parse(scansJson || '{}') - // - if we dont have a scans input + const scansToPerform = JSON.parse(scansJson || '[]') + // - if we don't have a scans input // or we do have a scans input, but it only has 1 item and its 'axe' // then we only want to run 'axe' and not the plugins // - keep in mind, 'onlyAxeScan' is not the same as 'shouldPerformAxeScan' diff --git a/.github/actions/find/tests/pluginManager.test.ts b/.github/actions/find/tests/pluginManager.test.ts index 70eff18..8e033d7 100644 --- a/.github/actions/find/tests/pluginManager.test.ts +++ b/.github/actions/find/tests/pluginManager.test.ts @@ -17,6 +17,11 @@ describe('loadPlugins', () => { vi.spyOn(fs, 'readdirSync').mockImplementation(readPath => { return [readPath + '/plugin-1', readPath + '/plugin-2'] }) + vi.spyOn(fs, 'lstatSync').mockImplementation(() => { + return { + isDirectory: () => true, + } as unknown as fs.Stats + }) }) describe('when plugins are not loaded', () => { From a1d409582ae88698af2bb1173f0eda9dd4408f51 Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 25 Feb 2026 09:10:42 -0500 Subject: [PATCH 63/65] update string literal --- .github/actions/find/src/pluginManager.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/find/src/pluginManager.ts b/.github/actions/find/src/pluginManager.ts index 6a64d80..46b2889 100644 --- a/.github/actions/find/src/pluginManager.ts +++ b/.github/actions/find/src/pluginManager.ts @@ -34,11 +34,11 @@ export async function loadPlugins() { } } -export const abortError = [ - 'There was an error while loading plugins.', - 'Clearing all plugins and aborting custom plugin scans.', - 'Please check the logs for hints as to what may have gone wrong.', -].join('\n') +export const abortError = ` +There was an error while loading plugins. +Clearing all plugins and aborting custom plugin scans. +Please check the logs for hints as to what may have gone wrong. +` export function clearCache() { pluginsLoaded = false From 64c996ca3d371297690d788496436567f8720a9a Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Wed, 25 Feb 2026 13:50:34 -0500 Subject: [PATCH 64/65] update code comments with more context --- .github/actions/find/src/dynamicImport.ts | 12 ++++++++++++ .github/actions/find/src/findForUrl.ts | 6 ++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/dynamicImport.ts b/.github/actions/find/src/dynamicImport.ts index caba867..1cf007d 100644 --- a/.github/actions/find/src/dynamicImport.ts +++ b/.github/actions/find/src/dynamicImport.ts @@ -1,9 +1,21 @@ // - this exists because I'm not sure how to mock // the dynamic import function, so mocking this instead +// (also, if it _is_ possible to mock the dynamic import, +// there's the risk of altering/breaking the behavior of imports +// across the board - including non-dynamic imports) +// // - also, vitest has a limitation on mocking: // https://vitest.dev/guide/mocking/modules.html#mocking-modules-pitfalls +// // - basically if a function is called by another function in the same file // it can't be mocked. So this was extracted into a separate file +// +// - one thing to note is vitest does the same thing here: +// https://github.com/vitest-dev/vitest/blob/main/test/core/src/dynamic-import.ts +// - and uses that with tests here: +// https://github.com/vitest-dev/vitest/blob/main/test/core/test/mock-internals.test.ts#L27 +// +// - so this looks like a reasonable approach export async function dynamicImport(path: string) { return import(path) } diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 0af96ad..2730b46 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -35,8 +35,10 @@ export async function findForUrl( rawFindings = await new AxeBuilder({page}).analyze() } - // - this condition is not required, but makes it easier to make assertions - // in unit tests on whether 'loadPlugins' was called or not + // - this if condition is not required, but makes it easier to make assertions + // in unit tests on whether 'loadPlugins' was called or not (it does have the added + // benefit of completely skipping plugin loading if we just want axe - so thats + // a minor performance boost) // - alternatively, we can wrap the 'plugin.default(...)' call in another function // and make assertions on whether that function was called or not // - the other option is to wrap each plugin in a class instance From 74f131f8a99063a67dc093be6ae61455cd72181a Mon Sep 17 00:00:00 2001 From: Abdul Ahmad Date: Thu, 26 Feb 2026 08:36:01 -0500 Subject: [PATCH 65/65] update comment --- .github/actions/find/src/dynamicImport.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/find/src/dynamicImport.ts b/.github/actions/find/src/dynamicImport.ts index 1cf007d..f00d224 100644 --- a/.github/actions/find/src/dynamicImport.ts +++ b/.github/actions/find/src/dynamicImport.ts @@ -1,5 +1,5 @@ -// - this exists because I'm not sure how to mock -// the dynamic import function, so mocking this instead +// - this exists because it looks like there's no straight-forward +// way to mock the dynamic import function, so mocking this instead // (also, if it _is_ possible to mock the dynamic import, // there's the risk of altering/breaking the behavior of imports // across the board - including non-dynamic imports)