Skip to content

ElementInternals: Create extension interface to pass internal data from extension #5040

@straker

Description

@straker

Create the axe.externalAPIs function which accepts how to handle passing information from the extension to the axe-core context. Add necessary tests.

axe.external = {};
axe.externalAPIs = ({ elementInternalsTimeout = 1000, elementInternals }) => {
  axe.external.elementInternals = (res, rej) => {
    let timeoutCalled = false;
    const timeout = setTimeout(() => {
      timeoutCalled = true;
      res();
    }, elementInternalsTimeout);

    return elementInternals()
      .then(results => {
        clearTimeout(timeout);

        // timeout already called so don't process the response
        if (timeoutCalled) {
          return;
        }

        // resolve ancestry strings to nodes
        for (const ancestry in results) {
          const node = document.querySelector(ancestry);
          const vNode = axe.utils.getNodeFromTree(node);
          const internals = results[ancestry];

          // convert idref(s) ancestries back to nodes
          for (const prop in internals) {
            if (prop.endsWith('Element')) {
              internals[prop] = document.querySelector(internals[prop])
            }
            else if (prop.endsWith('Elements')) {
              internals[prop] = internals[prop].map(node => document.querySelector(node));
            }
          }

          // set internals directly onto the vNode
          vNode.internals = internals;
        }

        res();
      })
      .catch(rej);
  };
}

class Audit {
  run(context, options, resolve, reject) {
    this.normalizeOptions(options);
    DqElement.setRunOptions(options);

    const internalsQueue = queue();
    if (axe.external.elementInternals) {
      internalsQueue.defer(axe.external.elementInternals);
    }

    // if there is no external api function the queue resolves immediately
    internalsQueue.then(() => {
      // rest of audit.run code
    })
  });
}

class VirtualNode {
  // move to getter so it's not automatically run when the node is created
  // and then cache the results similar to vNode.props
  get internals() {
    if (!this._cache.hasOwnProperty('internals')) {
      this._cache.internals = getElementInternals(this.actualNode);
    }

    return this._cache.internals;
  }

  set internals(value) {
    this._cache.internals = value;

1.   }

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions