Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/actions/find/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"}}}`

#### `reduced_motion`

**Optional** Playwright [`reducedMotion`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-reduced-motion) setting to emulate user preference. Allowed values: `reduce`, `no-preference`.

#### `color_scheme`

**Optional** Playwright [`colorScheme`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-color-scheme) setting to emulate user preference. Allowed values: `light`, `dark`, `no-preference`.

### Outputs

#### `findings`
Expand Down
6 changes: 6 additions & 0 deletions .github/actions/find/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ inputs:
description: "Whether to capture screenshots of scanned pages and include links to them in the issue"
required: false
default: "false"
reduced_motion:
description: "Playwright reducedMotion setting. Allowed values: 'reduce', 'no-preference'"
required: false
color_scheme:
description: "Playwright colorScheme setting. Allowed values: 'light', 'dark', 'no-preference'"
required: false

outputs:
findings:
Expand Down
8 changes: 7 additions & 1 deletion .github/actions/find/src/findForUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ export async function findForUrl(
url: string,
authContext?: AuthContext,
includeScreenshots: boolean = false,
reducedMotion?: 'reduce' | 'no-preference',
colorScheme?: 'light' | 'dark' | 'no-preference',
): Promise<Finding[]> {
const browser = await playwright.chromium.launch({
headless: true,
executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined,
})
const contextOptions = authContext?.toPlaywrightBrowserContextOptions() ?? {}
const contextOptions = {
Copy link
Copy Markdown
Contributor

@abdulahmad307 abdulahmad307 Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not blocking.

Since we're now generating a higher-level context object (that includes auth context and other settings), I think it would be valuable to have a 'contextBuilder' (or whatever you want to call it) that generates the full context outside of this function and passes in 1 context object (maybe even create a specific type of what we expect this context to contain). I would image somewhere in the main function in the index file, something like:

const context = buildFullContext(...whatYouDidHere)
findForUrl(context...)

side-note. this is more of a preference than a suggestion; we don't even have to pass the context down as a param if we don't want to. we can use a provider (contextProvider) that generates the context and caches it, and any function that needs it can import it and read it directly.

...(authContext?.toPlaywrightBrowserContextOptions() ?? {}),
...(reducedMotion ? {reducedMotion} : {}),
...(colorScheme ? {colorScheme} : {}),
}
const context = await browser.newContext(contextOptions)
const page = await context.newPage()
await page.goto(url)
Expand Down
18 changes: 17 additions & 1 deletion .github/actions/find/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,27 @@ export default async function () {
const authContext = new AuthContext(authContextInput)

const includeScreenshots = core.getInput('include_screenshots', {required: false}) !== 'false'
const reducedMotionInput = core.getInput('reduced_motion', {required: false})
Copy link
Copy Markdown
Contributor

@abdulahmad307 abdulahmad307 Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, non-blocking. so building on what I said in the comment before - most of this could be moved into a context builder function somewhere and called here.

I also personally find it valuable to wrap input reads in some kind of 'getter' or 'reader' function to isolate how each input is read/parsed (or if an error needs to be thrown, or if we need to transform the input somehow) before being exposed to the rest of the code (here's one similar example - although, this does a few more things than just read the input).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, once we've trimmed down the list of feature requests I think it would be a good time to do some code maintenance / refactoring (since the scope of the codebase is expanding). Perhaps once we've got your plugin functionality merged? That seems worthy of a major version update.

let reducedMotion: 'reduce' | 'no-preference' | undefined
if (reducedMotionInput) {
if (!['reduce', 'no-preference'].includes(reducedMotionInput)) {
throw new Error("Input 'reduced_motion' must be one of: 'reduce', 'no-preference'")
}
reducedMotion = reducedMotionInput as 'reduce' | 'no-preference'
}
const colorSchemeInput = core.getInput('color_scheme', {required: false})
let colorScheme: 'light' | 'dark' | 'no-preference' | undefined
if (colorSchemeInput) {
if (!['light', 'dark', 'no-preference'].includes(colorSchemeInput)) {
throw new Error("Input 'color_scheme' must be one of: 'light', 'dark', 'no-preference'")
}
colorScheme = colorSchemeInput as 'light' | 'dark' | 'no-preference'
}

const findings = []
for (const url of urls) {
core.info(`Preparing to scan ${url}`)
const findingsForUrl = await findForUrl(url, authContext, includeScreenshots)
const findingsForUrl = await findForUrl(url, authContext, includeScreenshots, reducedMotion, colorScheme)
if (findingsForUrl.length === 0) {
core.info(`No accessibility gaps were found on ${url}`)
continue
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
# auth_context: # Optional: Stringified JSON object for complex authentication
# skip_copilot_assignment: false # Optional: Set to true to skip assigning issues to GitHub Copilot (or if you don't have GitHub Copilot)
# include_screenshots: false # Optional: Set to true to capture screenshots and include links to them in filed issues
# reduced_motion: no-preference # Optional: Playwright reduced motion emulation (`reduce` or `no-preference`)
# color_scheme: dark # Optional: Playwright color scheme emulation (`light`, `dark`, or `no-preference`)
```

> 👉 Update all `REPLACE_THIS` placeholders with your actual values. See [Action Inputs](#action-inputs) for details.
Expand Down Expand Up @@ -115,6 +117,8 @@ Trigger the workflow manually or automatically based on your configuration. The
| `auth_context` | No | If scanned pages require authentication, a stringified JSON object containing username, password, cookies, and/or localStorage from an authenticated session | `{"username":"some-user","password":"***","cookies":[...]}` |
| `skip_copilot_assignment` | No | Whether to skip assigning filed issues to GitHub Copilot. Set to `true` if you don't have GitHub Copilot or prefer to handle issues manually | `true` |
| `include_screenshots` | No | Whether to capture screenshots of scanned pages and include links to them in filed issues. Screenshots are stored on the `gh-cache` branch of the repository running the workflow. Default: `false` | `true` |
| `reduced_motion` | No | Playwright `reducedMotion` setting for scan contexts. Allowed values: `reduce`, `no-preference` | `reduce` |
| `color_scheme` | No | Playwright `colorScheme` setting for scan contexts. Allowed values: `light`, `dark`, `no-preference` | `dark` |

---

Expand Down
8 changes: 8 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ inputs:
description: "Whether to capture screenshots and include links to them in the issue"
required: false
default: "false"
reduced_motion:
description: "Playwright reducedMotion setting for scans. Allowed values: 'reduce', 'no-preference'"
required: false
color_scheme:
description: "Playwright colorScheme setting for scans. Allowed values: 'light', 'dark', 'no-preference'"
required: false

outputs:
results:
Expand Down Expand Up @@ -85,6 +91,8 @@ runs:
urls: ${{ inputs.urls }}
auth_context: ${{ inputs.auth_context || steps.auth.outputs.auth_context }}
include_screenshots: ${{ inputs.include_screenshots }}
reduced_motion: ${{ inputs.reduced_motion }}
color_scheme: ${{ inputs.color_scheme }}
- name: File
id: file
uses: ./../../_actions/github/accessibility-scanner/current/.github/actions/file
Expand Down
Loading