Skip to content

Commit

Permalink
feat(adblock): add brave adblock support
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mendez committed May 15, 2023
1 parent 2a93667 commit a31cf81
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 159 deletions.
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

<img src="https://user-images.githubusercontent.com/8095978/236633334-f5234171-064e-4792-a21c-9e6c782ba9cc.jpg" height="50" align="right" padding="2px" />

Incredibly fast and precise web accessibility engine.
Incredibly fast and precise web accessibility engine with 0 dependencies.

```sh
npm install kayle --save
```

Playwright 🎭 or Puppeteer 🤖

```ts
import { kayle } from "kayle";

// Playwright 🎭 or Puppeteer 🤖
const page = await browser.newPage();

const results = await kayle({ page, browser, origin: "https://mywebsite.com" });
const results = await kayle({ page, browser, origin: "https://a11ywatch.com" });
```

If you need to run a full site-wide crawl import `autoKayle`.
Expand All @@ -36,9 +35,9 @@ const results = await autoKayle({
runners: ["htmlcs", "axe"],
includeWarnings: true,
origin: "https://a11ywatch.com",
cb: function callback(result) {
console.log(result)
}
cb: function callback(result) {
console.log(result);
},
// store: `${process.cwd()}/_data/`, // _data folder must exist first
});

Expand Down Expand Up @@ -159,11 +158,9 @@ the best aspects of testing without worrying about a `name`.
1. zh-CN ("Chinese-Simplified")
1. zh-TW ("Chinese-Traditional")

## Performance

This project is the fastest web accessibility runner OSS. The `htmlcs` and `axe-core` handling of the runners runs faster due to bug fixes and improved optimizations. This library optimizes the scripts to take advtage of v8 and pre-compiles locales in separate scripts for blazing fast speeds.
## Features

- Playwright runs 100% faster than puppeteer. Most of it is due to more fine grain control of events, ws connections, and timers.
You can enable a high performance adblock detection by brave by installing `npm i adblock-rs` to the project. This module needs to be manually installed and the env variable `KAYLE_ADBLOCK` needs to be set to `true`.

## Testing

Expand Down
5 changes: 2 additions & 3 deletions kayle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,13 @@ kayle supports multiple test runners which return different results. The built-i
- `htmlcs` (default): run tests using [HTML CodeSniffer](./lib/runners/htmlcs.ts)
- `custom`: custom runners.

## Benchmarks

## playwright
## Playwright/Puppeteer

`Fast_htmlcs`: expect runs to finish between 10-40ms with static html and around 30-90ms without.
`Fast_axecore`: expect runs to finish between 40-350ms with static html and around 30-90ms without.

We are working on making fast_axecore fast so it can run relatively near htmlcs.
If you are using puppeteer expect around 2x slower results.

## Utils

Expand Down
85 changes: 85 additions & 0 deletions kayle/lib/auto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { _log } from "./config";
import { Audit, kayle, RunnerConf } from "./kayle";

let write;
let extractLinks;

// on autoKayle link find callback
declare function callback(audit: Audit): Audit;
declare function callback(audit: Audit): Promise<Audit>;

/**
* Run accessibility tests for page auto running until all pages complete.
* @param {Object} [config={}] config - Options to change the way tests run.
* @returns {Promise} Returns a promise which resolves with array of results.
*/
export async function autoKayle(
o: RunnerConf & { log?: boolean; store?: string; cb?: typeof callback } = {},
ignoreSet?: Set<String>,
_results?: Audit[]
): Promise<Audit[]> {
if (!write) {
const { writeFile } = await import("fs/promises");
write = writeFile;
}
// pre init list
if (!_results) {
_results = [];
}

const result = await kayle(o, true);

_results.push(result);

if (o.cb && typeof o.cb === "function") {
await o.cb(result);
}

// auto run links until finished.
if (!extractLinks) {
extractLinks = (await import("./wasm/extract")).extractLinks;
}

if (!ignoreSet) {
ignoreSet = new Set();
}

const links: string[] = await extractLinks(o);

// persist html file to disk
if (o.store) {
await write(
`${o.store}/${encodeURIComponent(o.page.url())}`,
await o.page.content()
);
}

await o.page.close();

await Promise.all(
links.map(async (link) => {
if (ignoreSet.has(link)) {
return await Promise.resolve();
}

if (_log.enabled) {
console.log(`Running: ${link}`);
}

ignoreSet.add(link);

return await autoKayle(
{
...o,
page: await o.browser.newPage(),
html: null,
origin: link,
},
ignoreSet,
_results
);
})
);

return _results;
}
12 changes: 12 additions & 0 deletions kayle/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,15 @@ export type RunnerConfig = {
// prevent auto intercept request to prevent fetching resources.
noIntercept?: boolean;
};

// log singleton
export const _log = { enabled: false };

/**
* Enable or disable logging.
* @param {Object} [enabled] enabled - Enable console logging.
* @returns {void} Returns void.
*/
export function setLogging(enabled?: boolean): void {
_log.enabled = enabled;
}
5 changes: 5 additions & 0 deletions kayle/lib/data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# data

Things that help make kayle more efficient.

1. [adblock](https://github.com/brave/adblock-rust) - Enabled by using the `process.env.KAYLE_ADBLOCK=true` or setting the env variable `KAYLE_ADBLOCK` to true. In order to use adblock you also need to run `npm i adblock-rs` to install the wasm module locally.
2 changes: 2 additions & 0 deletions kayle/lib/data/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// get a list of valid things that help with data
export const adblock = Symbol("adblock engine to ignore resources");
12 changes: 3 additions & 9 deletions kayle/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
export {
kayle,
setLogging,
autoKayle,
Issue,
Audit,
MetaInfo,
Automatable,
} from "./kayle";
export { kayle, Issue, Audit, MetaInfo, Automatable } from "./kayle";
export { autoKayle } from "./auto";
export { runnersJavascript } from "./runner-js";
export {
goToPage,
setNetworkInterception,
networkBlock,
} from "./utils/go-to-page";
export { setLogging } from "./config";
176 changes: 43 additions & 133 deletions kayle/lib/kayle.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { extractArgs } from "./option";
import { runAction } from "./action";
import { RunnerConfig } from "./config";
import { RunnerConfig, _log } from "./config";
import { runnersJavascript, getRunner } from "./runner-js";
import { goToPage, setNetworkInterception } from "./utils/go-to-page";
import { Watcher } from "./watcher";
import { writeFile } from "fs/promises";

export type MetaInfo = {
errorCount: number;
Expand Down Expand Up @@ -41,137 +40,7 @@ export type Audit = {
pageUrl: string;
};

type RunnerConf = Partial<RunnerConfig & { html?: string }>;

let _log = false;

/**
* Enable or disable logging.
* @param {Object} [enabled] enabled - Enable console logging.
* @returns {void} Returns void.
*/
export function setLogging(enabled?: boolean): void {
_log = enabled;
}

/**
* Run accessibility tests for page.
* @param {Object} [config={}] config - Options to change the way tests run.
* @param {Boolean} [preventClose=false] preventClose - Prevent page page from closing on finish.
* @returns {Promise} Returns a promise which resolves with results.
*/
export async function kayle(
o: RunnerConf = {},
preventClose?: boolean
): Promise<Audit> {
const navigate =
typeof o.page.url === "function" &&
o.page.url() === "about:blank" &&
(o.origin || o.html);

// navigate to a clean page
if (navigate) {
await goToPage(
{ page: o.page, html: o.html, timeout: o.timeout },
o.origin
);
} else if (!o.noIntercept) {
await setNetworkInterception(o.page);
}

const config = extractArgs(o);

const watcher = new Watcher();

const results = await Promise.race([
watcher.watch(config.timeout),
auditPage(config),
]);

clearTimeout(watcher.timer);

!preventClose && navigate && (await o.page.close());

return results;
}

let extractLinks;

// on autoKayle link find callback
declare function callback(audit: Audit): Audit;
declare function callback(audit: Audit): Promise<Audit>;

/**
* Run accessibility tests for page auto running until all pages complete.
* @param {Object} [config={}] config - Options to change the way tests run.
* @returns {Promise} Returns a promise which resolves with array of results.
*/
export async function autoKayle(
o: RunnerConf & { log?: boolean; store?: string; cb?: typeof callback } = {},
ignoreSet?: Set<String>,
_results?: Audit[]
): Promise<Audit[]> {
// pre init list
if (!_results) {
_results = [];
}

const result = await kayle(o, true);

_results.push(result);

if (o.cb && typeof o.cb === "function") {
await o.cb(result);
}

// auto run links until finished.
if (!extractLinks) {
extractLinks = (await import("./wasm/extract")).extractLinks;
}

if (!ignoreSet) {
ignoreSet = new Set();
}

const links: string[] = await extractLinks(o);

// persist html file to disk
if (o.store) {
await writeFile(
`${o.store}/${encodeURIComponent(o.page.url())}`,
await o.page.content()
);
}

await o.page.close();

await Promise.all(
links.map(async (link) => {
if (ignoreSet.has(link)) {
return await Promise.resolve();
}

if (_log) {
console.log(`Running: ${link}`);
}

ignoreSet.add(link);

return await autoKayle(
{
...o,
page: await o.browser.newPage(),
html: null,
origin: link,
},
ignoreSet,
_results
);
})
);

return _results;
}
export type RunnerConf = Partial<RunnerConfig & { html?: string }>;

// run accessibility audit
async function auditPage(config: RunnerConfig) {
Expand Down Expand Up @@ -222,3 +91,44 @@ async function audit(config: RunnerConfig): Promise<Audit> {
}
);
}

/**
* Run accessibility tests for page.
* @param {Object} [config={}] config - Options to change the way tests run.
* @param {Boolean} [preventClose=false] preventClose - Prevent page page from closing on finish.
* @returns {Promise} Returns a promise which resolves with results.
*/
export async function kayle(
o: RunnerConf = {},
preventClose?: boolean
): Promise<Audit> {
const navigate =
typeof o.page.url === "function" &&
o.page.url() === "about:blank" &&
(o.origin || o.html);

// navigate to a clean page
if (navigate) {
await goToPage(
{ page: o.page, html: o.html, timeout: o.timeout },
o.origin
);
} else if (!o.noIntercept) {
await setNetworkInterception(o.page);
}

const config = extractArgs(o);

const watcher = new Watcher();

const results = await Promise.race([
watcher.watch(config.timeout),
auditPage(config),
]);

clearTimeout(watcher.timer);

!preventClose && navigate && (await o.page.close());

return results;
}
Loading

0 comments on commit a31cf81

Please sign in to comment.