Skip to content

Commit

Permalink
fix(puppeteer): get results from iframes with branding (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
AdnoC committed Jul 27, 2020
1 parent bbe6295 commit 7e71380
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 43 deletions.
24 changes: 13 additions & 11 deletions packages/puppeteer/src/axePuppeteer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Axe from 'axe-core';
import { ElementHandle, Frame, JSONObject, Page } from 'puppeteer';
import { pageIsLoaded, runAxe } from './browser';
import { pageIsLoaded, runAxe, configureAxe } from './browser';
import { AnalyzeCB } from './types';

function arrayify<T>(src: T | T[]): T[] {
Expand All @@ -11,27 +11,28 @@ function arrayify<T>(src: T | T[]): T[] {
}

interface IInjectAxeArgs {
source?: string;
source?: string | Function;
selector: string;
logOnError?: boolean;
args?: any[];
}

function injectAxeModule(frame: Frame): Promise<void> {
function injectJSModule(frame: Frame): Promise<void> {
return frame.addScriptTag({
path: require.resolve('axe-core')
});
}

function injectAxeString(frame: Frame, source: string): Promise<void> {
return frame.evaluate(source);
function injectJSSource(frame: Frame, source: string | Function, args: any[] = []): Promise<void> {
return frame.evaluate(source as any, ...args);
}

async function injectAxe(frame: Frame, {source, selector, logOnError}: IInjectAxeArgs): Promise<void> {
async function injectJS(frame: Frame, {source, selector, logOnError, args}: IInjectAxeArgs): Promise<void> {
const frames = await frame.$$(selector);
const injections = [];
for (const frameElement of frames) {
const subFrame = await frameElement.contentFrame();
const p = injectAxe(subFrame as Frame, { source, selector, logOnError: true});
const p = injectJS(subFrame as Frame, { source, selector, args, logOnError: true});
injections.push(p);
}

Expand All @@ -42,9 +43,9 @@ async function injectAxe(frame: Frame, {source, selector, logOnError}: IInjectAx

let injectP: Promise<void>;
if (!source) {
injectP = injectAxeModule(frame);
injectP = injectJSModule(frame);
} else {
injectP = injectAxeString(frame, source);
injectP = injectJSSource(frame, source, args);
}

if (logOnError) {
Expand Down Expand Up @@ -217,12 +218,13 @@ export class AxePuppeteer {
try {
await ensureFrameReady(this.frame);

await injectAxe(this.frame, { source: this.source, selector: this.iframeSelector()});
await injectJS(this.frame, { source: this.source, selector: this.iframeSelector()});

await injectJS(this.frame, { source: configureAxe, selector: this.iframeSelector(), args: [this.config] });

const context = normalizeContext(this.includes, this.excludes);
const axeResults = await this.frame.evaluate(
runAxe,
this.config as JSONObject,
context as JSONObject,
this.axeOptions as JSONObject
);
Expand Down
27 changes: 13 additions & 14 deletions packages/puppeteer/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,25 @@ declare global {

// Defined at top-level to clarify that it can't capture variables from outer scope.
export function runAxe(
config?: Axe.Spec,
context?: Axe.ElementContext,
options?: Axe.RunOptions
): Promise<Axe.AxeResults> {
if (config) {
window.axe.configure(config);
}

// This prevents axe from running in iframes.
// TODO: Uncomment when that is fixed in axe-core https://github.com/dequelabs/axe-core/issues/2340
// const brandingConfig = {
// branding: {
// application: 'axe-puppeteer'
// }
// };
// window.axe.configure(brandingConfig);

return window.axe.run(context || document, options || {});
}

export function pageIsLoaded(): boolean {
return document.readyState === 'complete';
}

export function configureAxe(config?: Axe.Spec) {
if (config) {
window.axe.configure(config);
}

const brandingConfig = {
branding: {
application: 'axe-puppeteer'
}
};
window.axe.configure(brandingConfig);
}
2 changes: 1 addition & 1 deletion packages/puppeteer/test/fixtures/frames/bar.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>Bar</title>
</head>
Expand Down
2 changes: 1 addition & 1 deletion packages/puppeteer/test/fixtures/frames/baz.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>Baz</title>
</head>
Expand Down
2 changes: 1 addition & 1 deletion packages/puppeteer/test/fixtures/frames/foo.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>Foo</title>
</head>
Expand Down
2 changes: 1 addition & 1 deletion packages/puppeteer/test/fixtures/nested-frames.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>Nested Frames</title>
</head>
Expand Down
43 changes: 38 additions & 5 deletions packages/puppeteer/test/integration/doc-dylang.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@

import { expect } from 'chai';
import Puppeteer from 'puppeteer';
import * as path from 'path';
import AxePuppeteer from '../../src/index';
import Axe from 'axe-core';
import { customConfig, fixtureFilePath } from '../utils';
import express from 'express';
import { createServer } from 'http';
import testListen from 'test-listen';

describe('doc-dylang.html', function () {
before(async function () {
Expand All @@ -19,20 +24,31 @@ describe('doc-dylang.html', function () {
after(async function () {
await this.browser.close();
});
before(async function () {
// const app: express.Application = express()
const app: express.Application = express();
app.use(express.static(path.resolve(__dirname, '..', 'fixtures')));
this.server = createServer(app);
this.addr = await testListen(this.server);

this.fixtureFileURL = (filename: string): string => {
return `${this.addr}/${filename}`;
};
});
after(function () {
this.server.close();
});
beforeEach(async function () {
this.page = await this.browser.newPage();
});
afterEach(async function () {
await this.page.close();
});

// Fails since we can't set branding due to a bug.
// TODO: Run once we fix setting branding https://github.com/dequelabs/axe-core/issues/2340
it.skip('should find violations with customized helpUrl', async function () {
const file = fixtureFilePath('doc-dylang.html');
it('should find violations with customized helpUrl', async function () {
const config = await customConfig();

await this.page.goto(`file://${file}`);
await this.page.goto(this.fixtureFileURL('doc-dylang.html'));

const results = await new AxePuppeteer(this.page)
.configure(config)
Expand All @@ -46,4 +62,21 @@ describe('doc-dylang.html', function () {
).to.not.eql(-1);
expect(results.passes).to.have.lengthOf(0);
});


it('configures in nested frames', async function() {
await this.page.goto(this.fixtureFileURL('nested-frames.html'))

const results = await new AxePuppeteer(this.page)
.configure(await customConfig())
.withRules(['dylang'])
.analyze()

expect(results.violations.find((r: Axe.Result) => r.id === 'dylang'))
.to.not.be.undefined
expect(results.violations.find((r: Axe.Result) => r.id === 'dylang'))
.to.have.property('nodes')
.and.to.have.lengthOf(4)
})

});
11 changes: 2 additions & 9 deletions packages/puppeteer/test/usage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Puppeteer from 'puppeteer';
import * as sinon from 'sinon';
import testListen from 'test-listen';
import AxePuppeteer, { loadPage } from '../src/index';
import { customConfig } from './utils';

type SinonSpy = sinon.SinonSpy;
type Frame = Puppeteer.Frame;
Expand Down Expand Up @@ -169,20 +170,12 @@ describe('AxePuppeteer', function () {
const results = await new AxePuppeteer(this.page)
.analyze()

const flatResults = [
...results.passes,
...results.incomplete,
...results.inapplicable,
...results.violations
]


expect(results.violations.find((r: Axe.Result) => r.id === 'label'))
.to.not.be.undefined
})


it('injects custom axe source into nexted frames', async function () {
it('injects custom axe source into nested frames', async function () {
const axeSource = `
window.axe = {
run: () => Promise.resolve({}),
Expand Down

0 comments on commit 7e71380

Please sign in to comment.