dequelabs / axe-core

Accessibility engine for automated Web UI testing
https://www.deque.com/axe/
Mozilla Public License 2.0
5.87k stars 763 forks source link

Testing iframes with Playwright/axe-core #4522

Open zuzanna-maria opened 2 months ago

zuzanna-maria commented 2 months ago

Product

axe-core

Product Version

No response

Latest Version

Issue Description

Expectation

I am working on accessibility testing with Playwright and @axe-core, following the instructions here. My page contains iframes and I would like to scan and analyze the content inside them.

Actual

When scanning the page object as per the instructions, the incomplete property of the accessibilityScanResults object contains this:

    {
      id: 'frame-tested',
      impact: 'critical',
      tags: [Array],
      description: 'Ensures <iframe> and <frame> elements contain the axe-core script',
      help: 'Frames should be tested with axe-core',
      helpUrl: 'https://dequeuniversity.com/rules/axe/4.9/frame-tested?application=playwright',
      nodes: [Array]
    }

The link provided under the helpUrl property says to "Ensure iframe and frame elements contain the axe-core script" but doesn't provide an example of how to achieve that.

How to Reproduce

When testing a page containing an iframe with code provided in the documentation:

const accessibilityScanResults = await new AxeBuilder({ page }).analyze()

the above issue occurs. it does not occur on pages without iframes. It happens even when trying to explicitly include the iframe as per the documentation:

const accessibilityScanResults = await new AxeBuilder({ page }).include('#sbox-iframe').analyze()

Additional context

Any thing else we should know about the issue?

straker commented 2 months ago

Thanks for the issue. Unfortunately there are serval reason why an iframe wouldn't be tested. If for some reason axe was prevented from loading into the iframe (for example due to CORS), if the iframe was not fully loaded or even lazy loaded (loading="lazy" and thus not loaded into view when the page loads), or if the iframe threw a JavaScript error during the axe.run execution. Without seeing the actual page it's hard to say what the issue is exactly.

You could try adding a wait to the script just to see if allowing the page more time to load fixes the issue.

import { timeout } from 'node:timers/promises'

await page.goto('https://your-site.com/')
await setTimeout(5000)
const accessibilityScanResults = await new AxeBuilder({ page }).analyze()
zuzanna-maria commented 2 months ago

Thanks for your reply! I am trying to analyze https://cryptpad.fr/code. It is indeed likely the injection is being blocked by security measures. Could you clarify what kind of injection is being performed by axe-core, as the documentation seems to lack detail on this?

Billy-Stroud commented 2 months ago

I am getting the same issue for an iFrame in a Salesforce application while using the @axe-core/playwright package. Is there a way to determine if the script isn't being loaded due to CORS?

straker commented 2 months ago

@Billy-Stroud Unfortunately at the moment there isn't a way to get at this information. We're in discussions to see if we could expose the actual error, but it won't be any time soon.

As a way to see what's going on, I have this custom script I use to inject axe into each frame on the page. You could try running it on the pages in question and hopefully see the same type of error that @axe-core/playwright would be running into.

function inject(doc) {
  const script = doc.createElement('script');
  script.src = 'https://unpkg.com/axe-core@latest';
  doc.body.appendChild(script);

  doc.querySelectorAll('iframe, frame').forEach(frame => {
    frame.contentDocument && inject(frame.contentDocument)
  });
}

inject(document)
Billy-Stroud commented 2 months ago

@straker Thank you! That allowed me to see the CORS error. It would definitely be helpful to expose these errors in the future.