Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  accessibility.spec.ts   Sprache: JAVA

 

/**
 * @license
 * Copyright 2018 Google Inc.
 * SPDX-License-Identifier: Apache-2.0
 */


import assert from 'node:assert';

import expect from 'expect';
import type {SerializedAXNode} from 'puppeteer-core/internal/cdp/Accessibility.js';

import {getTestState, setupTestBrowserHooks} from './mocha-utils.js';
import {attachFrame, html, htmlRaw} from './utils.js';

describe('Accessibility', function () {
  setupTestBrowserHooks();

  it('should work', async () => {
    const {page} = await getTestState();

    await page.setContent(
      htmlRaw`
        <!DOCTYPE html>
        <html lang="en">
        <head>
          <title>Accessibility Test</title>
        </head>
        <body>
          <div>Hello World</div>
          <h1>Inputs</h1>
          <input placeholder="Empty input" autofocus />
          <input placeholder="readonly input" readonly />
          <input placeholder="disabled input" disabled />
          <input aria-label="Input with whitespace" value="  " />
          <input value="value only" />
          <input aria-placeholder="placeholder" value="and a value" />
          <div aria-hidden="true" id="desc">This is a description!</div>
          <input
            aria-placeholder="placeholder"
            value="and a value"
            aria-describedby="desc"
          />
          <select>
            <option>First Option</option>
            <option>Second Option</option>
          </select>
          <a href="https://example.com">example</a>
        </body>`,
    );

    await page.focus('[placeholder="Empty input"]');
    expect(await page.accessibility.snapshot()).toMatchObject({
      role: 'RootWebArea',
      name: 'Accessibility Test',
      children: [
        {role: 'StaticText', name: 'Hello World'},
        {role: 'heading', name: 'Inputs', level: 1},
        {role: 'textbox', name: 'Empty input', focused: true},
        {role: 'textbox', name: 'readonly input', readonly: true},
        {role: 'textbox', name: 'disabled input', disabled: true},
        {role: 'textbox', name: 'Input with whitespace', value: '  '},
        {role: 'textbox', name: '', value: 'value only'},
        {role: 'textbox', name: 'placeholder', value: 'and a value'},
        {
          role: 'textbox',
          name: 'placeholder',
          value: 'and a value',
          description: 'This is a description!',
        },
        {
          role: 'combobox',
          name: '',
          value: 'First Option',
          haspopup: 'menu',
          expanded: false,
          children: [
            {role: 'option', name: 'First Option', selected: true},
            {role: 'option', name: 'Second Option'},
          ],
        },
        {
          name: 'example',
          role: 'link',
          url: 'https://example.com/',
        },
      ],
    });
  });

  it('should work for showcase', async () => {
    const {page, server} = await getTestState();
    await page.goto(server.PREFIX + '/a11y/landmarks.html');
    const snapshot = await page.accessibility.snapshot();
    expect(snapshot).toMatchObject({
      role: 'RootWebArea',
      name: 'HTML Elements Showcase',
      children: [
        {
          role: 'banner',
          name: '',
          children: [
            {
              role: 'heading',
              name: 'HTML Elements Showcase',
              level: 1,
            },
            {
              role: 'navigation',
              name: '',
              children: [
                {
                  role: 'link',
                  name: 'Forms',
                  children: [
                    {
                      role: 'StaticText',
                      name: 'Forms',
                    },
                  ],
                },
                {
                  role: 'link',
                  name: 'Media',
                  children: [
                    {
                      role: 'StaticText',
                      name: 'Media',
                    },
                  ],
                },
                {
                  role: 'link',
                  name: 'Interactive',
                  children: [
                    {
                      role: 'StaticText',
                      name: 'Interactive',
                    },
                  ],
                },
              ],
            },
          ],
        },
        {
          role: 'main',
          name: '',
          children: [
            {
              role: 'search',
              name: '',
            },
          ],
        },
        {
          role: 'complementary',
          name: '',
          children: [
            {
              role: 'StaticText',
              name: 'complementary',
            },
          ],
        },
        {
          role: 'contentinfo',
          name: '',
          children: [
            {
              role: 'StaticText',
              name: 'contentinfo',
            },
          ],
        },
      ],
    });
  });

  it('should report uninteresting nodes', async () => {
    const {page} = await getTestState();

    await page.setContent(html`<textarea>hi</textarea>`);
    await page.focus('textarea');
    expect(
      findFocusedNode(
        await page.accessibility.snapshot({interestingOnly: false}),
      ),
    ).toMatchObject({
      role: 'textbox',
      name: '',
      value: 'hi',
      focused: true,
      multiline: true,
      children: [
        {
          role: 'generic',
          name: '',
          children: [
            {
              role: 'StaticText',
              name: 'hi',
            },
          ],
        },
      ],
    });
  });
  it('get snapshots while the tree is re-calculated', async () => {
    // see https://github.com/puppeteer/puppeteer/issues/9404
    const {page} = await getTestState();

    await page.setContent(
      htmlRaw`<!DOCTYPE html>
        <html lang="en">
          <head>
            <meta charset="UTF-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <meta
              name="viewport"
              content="width=device-width, initial-scale=1.0"
            />
            <title>Accessible name + aria-expanded puppeteer bug</title>
            <style>
              [aria-expanded='false'] + * {
                display: none;
              }
            </style>
          </head>
          <body>
            <button hidden>Show</button>
            <p>Some content</p>
            <script>
              const button = document.querySelector('button');
              button.removeAttribute('hidden');
              button.setAttribute('aria-expanded', 'false');
              button.addEventListener('click', function () {
                button.setAttribute(
                  'aria-expanded',
                  button.getAttribute('aria-expanded') !== 'true',
                );
                if (button.getAttribute('aria-expanded') == 'true') {
                  button.textContent = 'Hide';
                } else {
                  button.textContent = 'Show';
                }
              });
            </script>
          </body>
        </html>`,
    );
    async function getAccessibleName(page: any, element: any) {
      return (await page.accessibility.snapshot({root: element})).name;
    }
    using button = await page.$('button');
    expect(await getAccessibleName(page, button)).toEqual('Show');
    await button?.click();
    await page.waitForSelector('aria/Hide');
  });
  it('roledescription', async () => {
    const {page} = await getTestState();

    await page.setContent(
      html`<div
        tabindex="-1"
        aria-roledescription="foo"
        >Hi</div
      >`,
    );
    const snapshot = await page.accessibility.snapshot();
    // See https://chromium-review.googlesource.com/c/chromium/src/+/3088862
    assert(snapshot);
    assert(snapshot.children);
    assert(snapshot.children[0]);
    expect(snapshot.children[0]!.roledescription).toBeUndefined();
  });
  it('orientation', async () => {
    const {page} = await getTestState();

    await page.setContent(
      html`<a
        href=""
        role="slider"
        aria-orientation="vertical"
        >11</a
      >`,
    );
    const snapshot = await page.accessibility.snapshot();
    assert(snapshot);
    assert(snapshot.children);
    assert(snapshot.children[0]);
    expect(snapshot.children[0]!.orientation).toEqual('vertical');
  });
  it('autocomplete', async () => {
    const {page} = await getTestState();

    await page.setContent(
      html`<input
        type="number"
        aria-autocomplete="list"
      />`,
    );
    const snapshot = await page.accessibility.snapshot();
    assert(snapshot);
    assert(snapshot.children);
    assert(snapshot.children[0]);
    expect(snapshot.children[0]!.autocomplete).toEqual('list');
  });
  it('multiselectable', async () => {
    const {page} = await getTestState();

    await page.setContent(
      html`<div
        role="grid"
        tabindex="-1"
        aria-multiselectable="true"
      >
        hey
      </div>`,
    );
    const snapshot = await page.accessibility.snapshot();
    assert(snapshot);
    assert(snapshot.children);
    assert(snapshot.children[0]);
    expect(snapshot.children[0]!.multiselectable).toEqual(true);
  });

  describe('iframes', () => {
    it('should not include iframe data if not requested', async () => {
      const {page, server} = await getTestState();
      await attachFrame(page, 'frame1', server.EMPTY_PAGE);
      const frame1 = page.frames()[1];
      await frame1!.evaluate(() => {
        const button = document.createElement('button');
        button.innerText = 'value1';
        document.body.appendChild(button);
      });
      const snapshot = await page.accessibility.snapshot({
        interestingOnly: true,
      });
      expect(snapshot).toMatchObject({
        role: 'RootWebArea',
        name: '',
      });
    });

    it('same-origin iframe (interesting only)', async () => {
      const {page, server} = await getTestState();
      await attachFrame(page, 'frame1', server.EMPTY_PAGE);
      const frame1 = page.frames()[1];
      await frame1!.evaluate(() => {
        const button = document.createElement('button');
        button.innerText = 'value1';
        document.body.appendChild(button);
      });
      const snapshot = await page.accessibility.snapshot({
        interestingOnly: true,
        includeIframes: true,
      });
      expect(snapshot).toMatchObject({
        role: 'RootWebArea',
        name: '',
        children: [
          {
            role: 'Iframe',
            name: '',
            children: [
              {
                role: 'RootWebArea',
                name: '',
                children: [
                  {
                    role: 'button',
                    name: 'value1',
                  },
                ],
              },
            ],
          },
        ],
      });
    });

    it('cross-origin iframe (interesting only)', async () => {
      const {page, server} = await getTestState();
      await attachFrame(
        page,
        'frame1',
        server.CROSS_PROCESS_PREFIX + '/empty.html',
      );
      const frame1 = page.frames()[1];
      await frame1!.evaluate(() => {
        const button = document.createElement('button');
        button.innerText = 'value1';
        document.body.appendChild(button);
      });
      const snapshot = await page.accessibility.snapshot({
        interestingOnly: true,
        includeIframes: true,
      });
      expect(snapshot).toMatchObject({
        role: 'RootWebArea',
        name: '',
        children: [
          {
            role: 'Iframe',
            name: '',
            children: [
              {
                role: 'RootWebArea',
                name: '',
                children: [
                  {
                    role: 'button',
                    name: 'value1',
                  },
                ],
              },
            ],
          },
        ],
      });
    });

    it('same-origin iframe (all nodes)', async () => {
      const {page, server} = await getTestState();
      await attachFrame(page, 'frame1', server.EMPTY_PAGE);
      const frame1 = page.frames()[1];
      await frame1!.evaluate(() => {
        const button = document.createElement('button');
        button.innerText = 'value1';
        document.body.appendChild(button);
      });
      const snapshot = await page.accessibility.snapshot({
        interestingOnly: false,
        includeIframes: true,
      });
      expect(snapshot).toMatchObject({
        role: 'RootWebArea',
        name: '',
        children: [
          {
            role: 'none',
            children: [
              {
                role: 'generic',
                name: '',
                children: [
                  {
                    role: 'Iframe',
                    name: '',
                    children: [
                      {
                        role: 'RootWebArea',
                        name: '',
                        children: [
                          {
                            role: 'none',
                            children: [
                              {
                                role: 'generic',
                                name: '',
                                children: [
                                  {
                                    role: 'button',
                                    name: 'value1',
                                    children: [
                                      {
                                        role: 'StaticText',
                                        name: 'value1',
                                        children: [
                                          {
                                            role: 'InlineTextBox',
                                          },
                                        ],
                                      },
                                    ],
                                  },
                                ],
                              },
                            ],
                          },
                        ],
                      },
                    ],
                  },
                ],
              },
            ],
          },
        ],
      });
    });
  });

  it('keyshortcuts', async () => {
    const {page} = await getTestState();

    await page.setContent(
      html`<div
        role="grid"
        tabindex="-1"
        aria-keyshortcuts="foo"
        >hey</div
      >`,
    );
    const snapshot = await page.accessibility.snapshot();
    assert(snapshot);
    assert(snapshot.children);
    assert(snapshot.children[0]);
    expect(snapshot.children[0]!.keyshortcuts).toEqual('foo');
  });
  describe('filtering children of leaf nodes', function () {
    it('should not report text nodes inside controls', async () => {
      const {page} = await getTestState();

      await page.setContent(
        html` <div role="tablist">
          <div
            role="tab"
            aria-selected="true"
            ><b>Tab1</b></div
          >
          <div role="tab">Tab2</div>
        </div>`,
      );

      expect(await page.accessibility.snapshot()).toMatchObject({
        role: 'RootWebArea',
        name: 'My test page',
        children: [
          {
            role: 'tab',
            name: 'Tab1',
            selected: true,
          },
          {
            role: 'tab',
            name: 'Tab2',
          },
        ],
      });
    });
    it('rich text editable fields should have children', async () => {
      const {page} = await getTestState();

      await page.setContent(
        html` <div contenteditable="true">
          Edit this image:
          <img
            src="fakeimage.png"
            alt="my fake image"
          />
        </div>`,
      );
      const snapshot = await page.accessibility.snapshot();
      assert(snapshot);
      assert(snapshot.children);
      expect(snapshot.children[0]).toMatchObject({
        role: 'generic',
        name: '',
        value: 'Edit this image: ',
        children: [
          {
            role: 'StaticText',
            name: 'Edit this image: ',
          },
          {
            role: 'image',
            name: 'my fake image',
          },
        ],
      });
    });
    it('rich text editable fields with role should have children', async () => {
      const {page} = await getTestState();

      await page.setContent(
        html` <div
          contenteditable="true"
          role="textbox"
        >
          Edit this image:
          <img
            src="fakeimage.png"
            alt="my fake image"
          />
        </div>`,
      );
      // Image node should not be exposed in contenteditable elements. See https://crbug.com/1324392.
      const snapshot = await page.accessibility.snapshot();
      assert(snapshot);
      assert(snapshot.children);
      expect(snapshot.children[0]).toMatchObject({
        role: 'textbox',
        name: '',
        value: 'Edit this image: ',
        multiline: true,
        children: [
          {
            role: 'StaticText',
            name: 'Edit this image: ',
          },
        ],
      });
    });

    // Firefox does not support contenteditable="plaintext-only".
    describe('plaintext contenteditable', function () {
      it('plain text field with role should not have children', async () => {
        const {page} = await getTestState();

        await page.setContent(
          html`<div
            contenteditable="plaintext-only"
            role="textbox"
            >Edit this image:<img
              src="fakeimage.png"
              alt="my fake image"
          /></div>`,
        );
        const snapshot = await page.accessibility.snapshot();
        assert(snapshot);
        assert(snapshot.children);
        expect(snapshot.children[0]).toMatchObject({
          role: 'textbox',
          name: '',
          value: 'Edit this image:',
          multiline: true,
        });
      });
    });
    it('non editable textbox with role and tabIndex and label should not have children', async () => {
      const {page} = await getTestState();

      await page.setContent(
        html`<div
          role="textbox"
          tabindex="0"
          aria-checked="true"
          aria-label="my favorite textbox"
        >
          this is the inner content
          <img
            alt="yo"
            src="fakeimg.png"
          />
        </div>`,
      );

      const snapshot = await page.accessibility.snapshot();
      assert(snapshot);
      assert(snapshot.children);
      expect(snapshot.children[0]).toMatchObject({
        role: 'textbox',
        name: 'my favorite textbox',
        value: 'this is the inner content ',
      });
    });
    it('checkbox with and tabIndex and label should not have children', async () => {
      const {page} = await getTestState();

      await page.setContent(
        html` <div
          role="checkbox"
          tabindex="0"
          aria-checked="true"
          aria-label="my favorite checkbox"
        >
          this is the inner content
          <img
            alt="yo"
            src="fakeimg.png"
          />
        </div>`,
      );
      const snapshot = await page.accessibility.snapshot();
      assert(snapshot);
      assert(snapshot.children);
      expect(snapshot.children[0]).toMatchObject({
        role: 'checkbox',
        name: 'my favorite checkbox',
        checked: true,
      });
    });
    it('checkbox without label should not have children', async () => {
      const {page} = await getTestState();

      await page.setContent(
        html`<div
          role="checkbox"
          aria-checked="true"
        >
          this is the inner content
          <img
            alt="yo"
            src="fakeimg.png"
          />
        </div>`,
      );
      const snapshot = await page.accessibility.snapshot();
      assert(snapshot);
      assert(snapshot.children);
      expect(snapshot.children[0]).toMatchObject({
        role: 'checkbox',
        name: 'this is the inner content yo',
        checked: true,
      });
    });

    describe('root option', function () {
      it('should work a button', async () => {
        const {page} = await getTestState();

        await page.setContent(html`<button>My Button</button>`);

        using button = (await page.$('button'))!;
        expect(await page.accessibility.snapshot({root: button})).toMatchObject(
          {
            role: 'button',
            name: 'My Button',
          },
        );
      });
      it('should work an input', async () => {
        const {page} = await getTestState();

        await page.setContent(
          html`<input
            title="My Input"
            value="My Value"
          />`,
        );

        using input = (await page.$('input'))!;
        expect(await page.accessibility.snapshot({root: input})).toMatchObject({
          role: 'textbox',
          name: 'My Input',
          value: 'My Value',
        });
      });
      it('should work a menu', async () => {
        const {page} = await getTestState();

        await page.setContent(html`
          <div
            role="menu"
            title="My Menu"
          >
            <div role="menuitem">First Item</div>
            <div role="menuitem">Second Item</div>
            <div role="menuitem">Third Item</div>
          </div>
        `);

        using menu = (await page.$('div[role="menu"]'))!;
        expect(await page.accessibility.snapshot({root: menu})).toMatchObject({
          role: 'menu',
          name: 'My Menu',
          children: [
            {role: 'menuitem', name: 'First Item'},
            {role: 'menuitem', name: 'Second Item'},
            {role: 'menuitem', name: 'Third Item'},
          ],
          orientation: 'vertical',
        });
      });
      it('should return null when the element is no longer in DOM', async () => {
        const {page} = await getTestState();

        await page.setContent(html`<button>My Button</button>`);
        using button = (await page.$('button'))!;
        await page.$eval('button', button => {
          return button.remove();
        });
        expect(await page.accessibility.snapshot({root: button})).toEqual(null);
      });
      it('should support the interestingOnly option', async () => {
        const {page} = await getTestState();

        await page.setContent(
          html`<div><button>My Button</button></div
            ><div class="uninteresting"></div>`,
        );
        using div = (await page.$('div.uninteresting'))!;
        expect(await page.accessibility.snapshot({root: div})).toEqual(null);
        using divWithButton = (await page.$('div'))!;
        expect(
          await page.accessibility.snapshot({root: divWithButton}),
        ).toMatchObject({
          name: 'My Button',
          role: 'button',
        });
        expect(
          await page.accessibility.snapshot({
            root: divWithButton,
            interestingOnly: false,
          }),
        ).toMatchObject({
          role: 'generic',
          name: '',
          children: [
            {
              role: 'button',
              name: 'My Button',
              children: [{role: 'StaticText', name: 'My Button'}],
            },
          ],
        });
      });
      it('should work with nested button inside h1 with interestingOnly:true', async () => {
        const {page} = await getTestState();

        await page.setContent(html`
          <main>
            <h2>
              <button>My Button</button>
            </h2>
          </main>
        `);

        using button = (await page.$('button'))!;
        expect(await page.accessibility.snapshot({root: button})).toMatchObject(
          {
            role: 'button',
            name: 'My Button',
          },
        );
      });
    });

    describe('elementHandle()', () => {
      it('should get an ElementHandle from a snapshot item', async () => {
        const {page} = await getTestState();

        await page.setContent(html`<button>My Button</button>`);

        using button = (await page.$('button'))!;
        const snapshot = await page.accessibility.snapshot({root: button});
        expect(snapshot).toMatchObject({
          role: 'button',
          name: 'My Button',
        });

        using buttonHandle = await snapshot!.elementHandle();
        expect(
          await buttonHandle?.evaluate(button => {
            return button.innerHTML;
          }),
        ).toEqual('My Button');
      });

      it('should get the parent ElementHandle from a text node accessibility node', async () => {
        const {page} = await getTestState();

        await page.setContent(html`<div><b>Hello, </b> world!</div>`);
        using div = (await page.$('div'))!;

        const parentSnapshot = await page.accessibility.snapshot({
          root: div,
          interestingOnly: false,
        });
        expect(parentSnapshot).toMatchObject({
          role: 'generic',
          name: '',
          children: [
            {role: 'StaticText', name: 'Hello, '},
            {role: 'StaticText', name: 'world!'},
          ],
        });

        using textNode = (await div.evaluateHandle(el => {
          return el.lastChild!;
        }))!;
        const snapshot = await page.accessibility.snapshot({root: textNode});
        expect(snapshot).toMatchObject({
          role: 'StaticText',
          name: 'world!',
        });

        // Get the element handle from the text node.
        // This should be the parent's handle, not the text node's.
        using parentNodeHandle = await parentSnapshot!.elementHandle();
        using textNodeHandle = await snapshot!.elementHandle();
        expect(parentNodeHandle).toEqual(textNodeHandle);

        expect(
          await textNodeHandle?.evaluate(button => {
            return button.innerHTML;
          }),
        ).toEqual('<b>Hello, </b> world!');
      });
    });

    it('should not report Document as leaf node', async () => {
      const {page} = await getTestState();

      await page.setContent(
        html`<main><span>Hello</span><div> </div><div>World</div></main>`,
      );

      const snapshot = await page.accessibility.snapshot();
      expect(snapshot).toMatchObject({
        role: 'RootWebArea',
        name: 'My test page',
        children: [
          {
            name: '',
            role: 'main',
            children: [
              {
                name: 'Hello',
                role: 'StaticText',
              },
              {
                name: 'World',
                role: 'StaticText',
              },
            ],
          },
        ],
      });
    });
  });

  function findFocusedNode(
    node: SerializedAXNode | null,
  ): SerializedAXNode | null {
    if (node?.focused) {
      return node;
    }
    for (const child of node?.children || []) {
      const focusedChild = findFocusedNode(child);
      if (focusedChild) {
        return focusedChild;
      }
    }
    return null;
  }
});

Messung V0.5 in Prozent
C=100 H=98 G=98

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-10-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126864
#Domains=1897691