Skip to content

Commit

Permalink
feat(utils): add getFrameContexts method (#2995)
Browse files Browse the repository at this point in the history
* feat(utils): add getFrameContexts method

* updates

* finish tests

* better

* revert playground

* fix ie11

* Update lib/core/base/context.js

Co-authored-by: Wilco Fiers <WilcoFiers@users.noreply.github.com>

* Update lib/core/public/load.js

Co-authored-by: Wilco Fiers <WilcoFiers@users.noreply.github.com>

* tests

Co-authored-by: Steven Lambert <steven.lambert@deque.com>
Co-authored-by: Steven Lambert <2433219+straker@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 24, 2021
1 parent 70c683c commit f478bab
Show file tree
Hide file tree
Showing 4 changed files with 307 additions and 18 deletions.
38 changes: 20 additions & 18 deletions lib/core/base/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,31 @@ function pushUniqueFrame(collection, frame) {
function pushUniqueFrameSelector(context, type, selectorArray) {
context.frames = context.frames || [];

var result, frame;
var frames = document.querySelectorAll(selectorArray.shift());
const frameSelector = selectorArray.shift();
const frames = document.querySelectorAll(frameSelector);

frameloop: for (var i = 0, l = frames.length; i < l; i++) {
frame = frames[i];
for (var j = 0, l2 = context.frames.length; j < l2; j++) {
if (context.frames[j].node === frame) {
context.frames[j][type].push(selectorArray);
break frameloop;
Array.from(frames).forEach(frame => {
context.frames.forEach(contextFrame => {
if (contextFrame.node === frame) {
contextFrame[type].push(selectorArray);
}
}
result = {
node: frame,
include: [],
exclude: []
};
});

if (selectorArray) {
result[type].push(selectorArray);
if (!context.frames.find(result => result.node === frame)) {
const result = {
node: frame,
include: [],
exclude: []
};

if (selectorArray) {
result[type].push(selectorArray);
}

context.frames.push(result);
}
});

context.frames.push(result);
}
}

/**
Expand Down
12 changes: 12 additions & 0 deletions lib/core/utils/get-frame-contexts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Context from '../base/context';
import getAncestry from './get-ancestry';

export default function getFrameContexts(context) {
const { frames } = new Context(context);
return frames.map(({ node, include, exclude }) => {
const frameContext = { include, exclude };
frameContext.initiator = false;
const frameSelector = getAncestry(node);
return { frameSelector, frameContext };
});
}
1 change: 1 addition & 0 deletions lib/core/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export { default as getAllChecks } from './get-all-checks';
export { default as getBaseLang } from './get-base-lang';
export { default as getCheckMessage } from './get-check-message';
export { default as getCheckOption } from './get-check-option';
export { default as getFrameContexts } from './get-frame-contexts';
export { default as getFriendlyUriEnd } from './get-friendly-uri-end';
export { default as getNodeAttributes } from './get-node-attributes';
export { default as getNodeFromTree } from './get-node-from-tree';
Expand Down
274 changes: 274 additions & 0 deletions test/core/utils/get-frame-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
describe('utils.getFrameContexts', function() {
var getFrameContexts = axe.utils.getFrameContexts;
var shadowSupported = axe.testUtils.shadowSupport.v1;
var fixture = document.querySelector('#fixture');

it('returns an empty array if the page has no frames', function() {
var frameContext = getFrameContexts();
assert.isArray(frameContext);
assert.lengthOf(frameContext, 0);
});

it('sets context.initiator to false for each included frame', function() {
fixture.innerHTML =
'<iframe></iframe>' + '<iframe></iframe>' + '<iframe></iframe>';

var contexts = getFrameContexts().map(function(frameData) {
return frameData.frameContext;
});

assert.lengthOf(contexts, 3);
assert.isFalse(contexts[0].initiator);
assert.isFalse(contexts[1].initiator);
assert.isFalse(contexts[2].initiator);
});

it('returns a `frameSelector` for each included frame', function() {
fixture.innerHTML =
'<iframe></iframe>' + '<iframe></iframe>' + '<iframe></iframe>';

var selectors = getFrameContexts().map(function(frameData) {
return frameData.frameSelector;
});
assert.lengthOf(selectors, 3);
assert.include(selectors[0], 'iframe:nth-child(1)');
assert.include(selectors[1], 'iframe:nth-child(2)');
assert.include(selectors[2], 'iframe:nth-child(3)');
});

it('returns a `frameContext` for each included frame', function() {
fixture.innerHTML =
'<iframe id="f1"></iframe>' +
'<iframe id="f2"></iframe>' +
'<iframe id="f3"></iframe>';
var context = {
include: [
['#f1', 'header'],
['#f2', 'main']
],
exclude: [['#f3', 'footer']]
};
var contexts = getFrameContexts(context).map(function(frameData) {
return frameData.frameContext;
});

assert.lengthOf(contexts, 3);
assert.deepEqual(contexts[0], {
initiator: false,
include: [['header']],
exclude: []
});
assert.deepEqual(contexts[1], {
initiator: false,
include: [['main']],
exclude: []
});
assert.deepEqual(contexts[2], {
initiator: false,
include: [],
exclude: [['footer']]
});
});

it('excludes non-frame contexts', function() {
fixture.innerHTML = '<iframe id="f1"></iframe>';
var context = {
include: [['#header'], ['a'], ['#f1', 'header']]
};
var contexts = getFrameContexts(context).map(function(frameData) {
return frameData.frameContext;
});

assert.lengthOf(contexts, 1);
assert.deepEqual(contexts[0], {
initiator: false,
include: [['header']],
exclude: []
});
});

it('mixes contexts if the frame is selected twice', function() {
fixture.innerHTML =
'<iframe id="f1"></iframe>' + '<iframe id="f2"></iframe>';
var context = {
include: [
['#f1', 'header'],
['#f2', 'footer']
],
exclude: [['iframe', 'main']]
};
var contexts = getFrameContexts(context).map(function(frameData) {
return frameData.frameContext;
});
assert.lengthOf(contexts, 2);
assert.deepEqual(contexts[0], {
initiator: false,
include: [['header']],
exclude: [['main']]
});
assert.deepEqual(contexts[1], {
initiator: false,
include: [['footer']],
exclude: [['main']]
});
});

it('combines include/exclude arrays of frames selected twice', function() {
fixture.innerHTML = '<iframe></iframe>';
var context = {
include: [
['iframe', 'header'],
['iframe', 'main']
],
exclude: [
['iframe', 'aside'],
['iframe', 'footer']
]
};
var contexts = getFrameContexts(context).map(function(frameData) {
return frameData.frameContext;
});

assert.lengthOf(contexts, 1);
assert.deepEqual(contexts[0], {
initiator: false,
include: [['header'], ['main']],
exclude: [['aside'], ['footer']]
});
});

it('skips excluded frames', function() {
fixture.innerHTML =
'<iframe id="f1"></iframe>' +
'<iframe id="f2"></iframe>' +
'<iframe id="f3"></iframe>';
var context = {
exclude: [[['#f2']]]
};
var selectors = getFrameContexts(context).map(function(frameData) {
return frameData.frameSelector;
});
assert.lengthOf(selectors, 2);
assert.include(selectors[0], 'iframe:nth-child(1)');
assert.include(selectors[1], 'iframe:nth-child(3)');
});

it('skips frames excluded by a parent', function() {
fixture.innerHTML = '<iframe></iframe>';
var frameContexts = getFrameContexts({
exclude: [['#fixture']]
});
assert.lengthOf(frameContexts, 0);
});

it('normalizes the context', function() {
var frameContexts;
fixture.innerHTML =
'<iframe id="f1"></iframe>' + '<iframe id="f2"></iframe>';
frameContexts = getFrameContexts('#f1');
assert.lengthOf(frameContexts, 1);
assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');
assert.deepEqual(frameContexts[0].frameContext, {
initiator: false,
include: [],
exclude: []
});

frameContexts = getFrameContexts({ include: ['#f1'] });
assert.lengthOf(frameContexts, 1);
assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');
assert.deepEqual(frameContexts[0].frameContext, {
initiator: false,
include: [],
exclude: []
});

frameContexts = getFrameContexts({ exclude: ['#f2'] });
assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');
assert.lengthOf(frameContexts, 1);
assert.deepEqual(frameContexts[0].frameContext, {
initiator: false,
include: [],
exclude: []
});
});

it('accepts elements', function() {
var frameContexts;
fixture.innerHTML =
'<iframe id="f1"></iframe>' + '<iframe id="f2"></iframe>';
var f1 = fixture.querySelector('#f1');
var f2 = fixture.querySelector('#f2');
frameContexts = getFrameContexts(f1);
assert.lengthOf(frameContexts, 1);
assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');
assert.deepEqual(frameContexts[0].frameContext, {
initiator: false,
include: [],
exclude: []
});

frameContexts = getFrameContexts({ include: [f1] });
assert.lengthOf(frameContexts, 1);
assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');
assert.deepEqual(frameContexts[0].frameContext, {
initiator: false,
include: [],
exclude: []
});

frameContexts = getFrameContexts({ exclude: [f2] });
assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');
assert.lengthOf(frameContexts, 1);
assert.deepEqual(frameContexts[0].frameContext, {
initiator: false,
include: [],
exclude: []
});
});

it('works with nested frames', function() {
fixture.innerHTML =
'<iframe id="f1"></iframe>' + '<iframe id="f2"></iframe>';
var context = {
include: [
['#f1', '#f3', 'header'],
['#f2', '#f4', '#f5', 'footer']
],
exclude: [['#f2', '#f6', '#f7', '#f7', 'main']]
};
var contexts = getFrameContexts(context).map(function(frameData) {
return frameData.frameContext;
});

assert.lengthOf(contexts, 2);
assert.deepEqual(contexts[0], {
initiator: false,
include: [['#f3', 'header']],
exclude: []
});
assert.deepEqual(contexts[1], {
initiator: false,
include: [['#f4', '#f5', 'footer']],
exclude: [['#f6', '#f7', '#f7', 'main']]
});
});

(shadowSupported ? it : xit)('works on iframes in shadow dom', function() {
fixture.innerHTML = '<div id="shadow"></div>';
var div = fixture.querySelector('div');
var shadowRoot = div.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = '<main><iframe id="f1"></iframe></main>';

var frameContext = getFrameContexts();

assert.lengthOf(frameContext, 1);
assert.lengthOf(frameContext[0].frameSelector, 2);
assert.equal(frameContext[0].frameSelector[1], 'main > iframe');
assert.deepEqual(frameContext[0].frameContext, {
initiator: false,
include: [],
exclude: []
});
});
});

0 comments on commit f478bab

Please sign in to comment.