Skip to content

Commit aefd2f4

Browse files
committed
update tests
- refactor code - restructure files for testing
1 parent ec52e36 commit aefd2f4

File tree

6 files changed

+203
-176
lines changed

6 files changed

+203
-176
lines changed

.github/actions/find/src/findForUrl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {AxeBuilder} from '@axe-core/playwright'
33
import playwright from 'playwright'
44
import {AuthContext} from './AuthContext.js'
55
import {generateScreenshots} from './generateScreenshots.js'
6-
import {loadPlugins, invokePlugin} from './pluginManager.js'
6+
import {loadPlugins, invokePlugin} from './pluginManager'
77
import {getScansContext} from './scansContextProvider.js'
88
import * as core from '@actions/core'
99

.github/actions/find/src/pluginManager.ts renamed to .github/actions/find/src/pluginManager/index.ts

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11
import * as fs from 'fs'
22
import * as path from 'path'
33
import {fileURLToPath} from 'url'
4-
import * as esbuild from 'esbuild'
5-
import {dynamicImport} from './dynamicImport.js'
6-
import type {Finding} from './types.d.js'
7-
import playwright from 'playwright'
84
import * as core from '@actions/core'
5+
import {
6+
loadPluginViaJsFile,
7+
loadPluginViaTsFile,
8+
} from './pluginFileLoaders.js'
9+
import {
10+
Plugin,
11+
PluginDefaultParams
12+
} from './types.d.js'
13+
914

1015
// Helper to get __dirname equivalent in ES Modules
1116
const __filename = fileURLToPath(import.meta.url)
1217
const __dirname = path.dirname(__filename)
1318

14-
type PluginDefaultParams = {
15-
page: playwright.Page
16-
addFinding: (findingData: Finding) => Promise<void>
17-
}
18-
19-
type Plugin = {
20-
name: string
21-
default: (options: PluginDefaultParams) => Promise<void>
22-
}
2319

2420
// Built-in plugin names shipped with the scanner.
2521
// Used to skip duplicates when loading custom plugins.
2622
const BUILT_IN_PLUGINS = ['reflow-scan']
2723

28-
const plugins: Plugin[] = []
24+
export const plugins: Plugin[] = []
25+
// - for unit tests.
26+
// - I (Abdul) am hesitant about exporting the plugins
27+
// variable directly because it introduces coupling.
28+
export function getPlugins() {
29+
return plugins
30+
}
2931
let pluginsLoaded = false
3032

3133
export async function loadPlugins() {
@@ -92,8 +94,8 @@ export async function loadPluginsFromPath({
9294
skipBuiltInPlugins?: string[]
9395
}) {
9496
try {
95-
const res = fs.readdirSync(pluginsPath)
96-
for (const pluginFolder of res) {
97+
const folders = fs.readdirSync(pluginsPath)
98+
for (const pluginFolder of folders) {
9799
const pluginFolderPath = path.join(pluginsPath, pluginFolder)
98100

99101
if (fs.existsSync(pluginFolderPath) && fs.lstatSync(pluginFolderPath).isDirectory()) {
@@ -126,45 +128,6 @@ export async function loadPluginsFromPath({
126128
}
127129
}
128130

129-
async function loadPluginViaTsFile(pluginFolderPath: string) {
130-
const pluginEntryPath = path.join(pluginFolderPath, 'index.ts')
131-
if (!fs.existsSync(pluginEntryPath)) {
132-
core.info(`No index.ts found for plugin at path: ${pluginFolderPath}`)
133-
return
134-
}
135-
136-
core.info(`index.ts found for plugin at path: ${pluginFolderPath}`)
137-
const esbuildResult = await esbuild.build({
138-
entryPoints: [pluginEntryPath],
139-
write: false,
140-
bundle: true,
141-
format: 'esm',
142-
platform: 'node',
143-
target: 'node24',
144-
sourcemap: 'inline',
145-
})
146-
147-
const outputFileContents = esbuildResult.outputFiles[0]?.text
148-
if (!outputFileContents) {
149-
core.info(`esbuild produced no output for plugin: ${pluginEntryPath}`)
150-
return
151-
}
152-
153-
const base64CompiledPlugin = Buffer.from(outputFileContents).toString('base64')
154-
return dynamicImport(`data:text/javascript;base64,${base64CompiledPlugin}`)
155-
}
156-
157-
async function loadPluginViaJsFile(pluginFolderPath: string) {
158-
const pluginEntryPath = path.join(pluginFolderPath, 'index.js')
159-
if (!fs.existsSync(pluginEntryPath)) {
160-
core.info(`No index.js found for plugin at path: ${pluginFolderPath}`)
161-
return
162-
}
163-
164-
core.info(`index.js found for plugin at path: ${pluginFolderPath}`)
165-
return dynamicImport(pluginEntryPath)
166-
}
167-
168131
type InvokePluginParams = PluginDefaultParams & {
169132
plugin: Plugin
170133
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as fs from 'fs'
2+
import * as path from 'path'
3+
import * as esbuild from 'esbuild'
4+
import {dynamicImport} from '../dynamicImport.js'
5+
import * as core from '@actions/core'
6+
import { Plugin } from './types.js'
7+
8+
// - these functions had to be moved into a separate file
9+
// because vitest will not mock the implementation of functions
10+
// that are in the same file as the function under test.
11+
// - https://vitest.dev/guide/mocking/modules.html#mocking-modules-pitfalls
12+
export async function loadPluginViaTsFile(pluginFolderPath: string): Promise<Plugin | undefined> {
13+
const pluginEntryPath = path.join(pluginFolderPath, 'index.ts')
14+
if (!fs.existsSync(pluginEntryPath)) {
15+
core.info(`No index.ts found for plugin at path: ${pluginFolderPath}`)
16+
return
17+
}
18+
19+
core.info(`index.ts found for plugin at path: ${pluginFolderPath}`)
20+
const esbuildResult = await esbuild.build({
21+
entryPoints: [pluginEntryPath],
22+
write: false,
23+
bundle: true,
24+
format: 'esm',
25+
platform: 'node',
26+
target: 'node24',
27+
sourcemap: 'inline',
28+
})
29+
30+
const outputFileContents = esbuildResult.outputFiles[0]?.text
31+
if (!outputFileContents) {
32+
core.info(`esbuild produced no output for plugin: ${pluginEntryPath}`)
33+
return
34+
}
35+
36+
const base64CompiledPlugin = Buffer.from(outputFileContents).toString('base64')
37+
return dynamicImport(`data:text/javascript;base64,${base64CompiledPlugin}`)
38+
}
39+
40+
export async function loadPluginViaJsFile(pluginFolderPath: string): Promise<Plugin | undefined> {
41+
const pluginEntryPath = path.join(pluginFolderPath, 'index.js')
42+
if (!fs.existsSync(pluginEntryPath)) {
43+
core.info(`No index.js found for plugin at path: ${pluginFolderPath}`)
44+
return
45+
}
46+
47+
core.info(`index.js found for plugin at path: ${pluginFolderPath}`)
48+
return dynamicImport(pluginEntryPath)
49+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type {Finding} from './types.d.js'
2+
import playwright from 'playwright'
3+
4+
type PluginDefaultParams = {
5+
page: playwright.Page
6+
addFinding: (findingData: Finding) => Promise<void>
7+
}
8+
9+
export type Plugin = {
10+
name: string
11+
default: (options: PluginDefaultParams) => Promise<void>
12+
}

.github/actions/find/tests/findForUrl.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import * as core from '@actions/core'
33
import {findForUrl} from '../src/findForUrl.js'
44
import {AxeBuilder} from '@axe-core/playwright'
55
import axe from 'axe-core'
6-
import * as pluginManager from '../src/pluginManager.js'
6+
import * as pluginManager from '../src/pluginManager'
7+
import { Plugin } from '../src/pluginManager/types.js'
78
import {clearCache} from '../src/scansContextProvider.js'
89

910
vi.mock('@actions/core', {spy: true})
@@ -33,7 +34,7 @@ vi.mock('@axe-core/playwright', () => {
3334
})
3435

3536
let actionInput: string = ''
36-
let loadedPlugins: pluginManager.Plugin[] = []
37+
let loadedPlugins: Plugin[] = []
3738

3839
function clearAll() {
3940
clearCache()

0 commit comments

Comments
 (0)