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


Quelle  queryhandler.spec.ts   Sprache: unbekannt

 
/**
 * @license
 * Copyright 2018 Google Inc.
 * SPDX-License-Identifier: Apache-2.0
 */
import assert from 'assert';

import expect from 'expect';
import {Puppeteer} from 'puppeteer-core';
import type {ElementHandle} from 'puppeteer-core/internal/api/ElementHandle.js';

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

describe('Query handler tests', function () {
  setupTestBrowserHooks();

  describe('Pierce selectors', function () {
    async function setUpPage(): ReturnType<typeof getTestState> {
      const state = await getTestState();
      await state.page.setContent(
        `<script>
         const div = document.createElement('div');
         const shadowRoot = div.attachShadow({mode: 'open'});
         const div1 = document.createElement('div');
         div1.textContent = 'Hello';
         div1.className = 'foo';
         const div2 = document.createElement('div');
         div2.textContent = 'World';
         div2.className = 'foo';
         shadowRoot.appendChild(div1);
         shadowRoot.appendChild(div2);
         document.documentElement.appendChild(div);
         </script>`,
      );
      return state;
    }
    it('should find first element in shadow', async () => {
      const {page} = await setUpPage();
      using div = (await page.$('pierce/.foo')) as ElementHandle<HTMLElement>;
      const text = await div.evaluate(element => {
        return element.textContent;
      });
      expect(text).toBe('Hello');
    });
    it('should find all elements in shadow', async () => {
      const {page} = await setUpPage();
      const divs = (await page.$$('pierce/.foo')) as Array<
        ElementHandle<HTMLElement>
      >;
      const text = await Promise.all(
        divs.map(div => {
          return div.evaluate(element => {
            return element.textContent;
          });
        }),
      );
      expect(text.join(' ')).toBe('Hello World');
    });
    it('should find first child element', async () => {
      const {page} = await setUpPage();
      using parentElement = (await page.$('html > div'))!;
      using childElement = (await parentElement.$(
        'pierce/div',
      )) as ElementHandle<HTMLElement>;
      const text = await childElement.evaluate(element => {
        return element.textContent;
      });
      expect(text).toBe('Hello');
    });
    it('should find all child elements', async () => {
      const {page} = await setUpPage();
      using parentElement = (await page.$('html > div'))!;
      const childElements = (await parentElement.$$('pierce/div')) as Array<
        ElementHandle<HTMLElement>
      >;
      const text = await Promise.all(
        childElements.map(div => {
          return div.evaluate(element => {
            return element.textContent;
          });
        }),
      );
      expect(text.join(' ')).toBe('Hello World');
    });
  });

  describe('Text selectors', function () {
    describe('in Page', function () {
      it('should query existing element', async () => {
        const {page} = await getTestState();

        await page.setContent('<section>test</section>');

        expect(await page.$('text/test')).toBeTruthy();
        expect(await page.$$('text/test')).toHaveLength(1);
      });
      it('should return empty array for non-existing element', async () => {
        const {page} = await getTestState();

        expect(await page.$('text/test')).toBeFalsy();
        expect(await page.$$('text/test')).toHaveLength(0);
      });
      it('should return first element', async () => {
        const {page} = await getTestState();

        await page.setContent('<div id="1">a</div><div>a</div>');

        using element = await page.$('text/a');
        expect(
          await element?.evaluate(e => {
            return e.id;
          }),
        ).toBe('1');
      });
      it('should return multiple elements', async () => {
        const {page} = await getTestState();

        await page.setContent('<div>a</div><div>a</div>');

        const elements = await page.$$('text/a');
        expect(elements).toHaveLength(2);
      });
      it('should pierce shadow DOM', async () => {
        const {page} = await getTestState();

        await page.evaluate(() => {
          const div = document.createElement('div');
          const shadow = div.attachShadow({mode: 'open'});
          const diva = document.createElement('div');
          shadow.append(diva);
          const divb = document.createElement('div');
          shadow.append(divb);
          diva.innerHTML = 'a';
          divb.innerHTML = 'b';
          document.body.append(div);
        });

        using element = await page.$('text/a');
        expect(
          await element?.evaluate(e => {
            return e.textContent;
          }),
        ).toBe('a');
      });
      it('should query deeply nested text', async () => {
        const {page} = await getTestState();

        await page.setContent('<div><div>a</div><div>b</div></div>');

        using element = await page.$('text/a');
        expect(
          await element?.evaluate(e => {
            return e.textContent;
          }),
        ).toBe('a');
      });
      it('should query inputs', async () => {
        const {page} = await getTestState();

        await page.setContent('<input value="a">');

        using element = (await page.$(
          'text/a',
        )) as ElementHandle<HTMLInputElement>;
        expect(
          await element?.evaluate(e => {
            return e.value;
          }),
        ).toBe('a');
      });
      it('should not query radio', async () => {
        const {page} = await getTestState();

        await page.setContent('<radio value="a">');

        expect(await page.$('text/a')).toBeNull();
      });
      it('should query text spanning multiple elements', async () => {
        const {page} = await getTestState();

        await page.setContent('<div><span>a</span> <span>b</span><div>');

        using element = await page.$('text/a b');
        expect(
          await element?.evaluate(e => {
            return e.textContent;
          }),
        ).toBe('a b');
      });
      it('should clear caches', async () => {
        const {page} = await getTestState();

        await page.setContent(
          '<div id=target1>text</div><input id=target2 value=text><div id=target3>text</div>',
        );
        using div = (await page.$('#target1')) as ElementHandle<HTMLDivElement>;
        using input = (await page.$(
          '#target2',
        )) as ElementHandle<HTMLInputElement>;

        await div.evaluate(div => {
          div.textContent = 'text';
        });
        expect(
          await page.$eval(`text/text`, e => {
            return e.id;
          }),
        ).toBe('target1');
        await div.evaluate(div => {
          div.textContent = 'foo';
        });
        expect(
          await page.$eval(`text/text`, e => {
            return e.id;
          }),
        ).toBe('target2');
        await input.evaluate(input => {
          input.value = '';
        });
        await input.type('foo');
        expect(
          await page.$eval(`text/text`, e => {
            return e.id;
          }),
        ).toBe('target3');

        await div.evaluate(div => {
          div.textContent = 'text';
        });
        await input.evaluate(input => {
          input.value = '';
        });
        await input.type('text');
        expect(
          await page.$$eval(`text/text`, es => {
            return es.length;
          }),
        ).toBe(3);
        await div.evaluate(div => {
          div.textContent = 'foo';
        });
        expect(
          await page.$$eval(`text/text`, es => {
            return es.length;
          }),
        ).toBe(2);
        await input.evaluate(input => {
          input.value = '';
        });
        await input.type('foo');
        expect(
          await page.$$eval(`text/text`, es => {
            return es.length;
          }),
        ).toBe(1);
      });
    });
    describe('in ElementHandles', function () {
      it('should query existing element', async () => {
        const {page} = await getTestState();

        await page.setContent('<div class="a"><span>a</span></div>');

        using elementHandle = (await page.$('div'))!;
        expect(await elementHandle.$(`text/a`)).toBeTruthy();
        expect(await elementHandle.$$(`text/a`)).toHaveLength(1);
      });

      it('should return null for non-existing element', async () => {
        const {page} = await getTestState();

        await page.setContent('<div class="a"></div>');

        using elementHandle = (await page.$('div'))!;
        expect(await elementHandle.$(`text/a`)).toBeFalsy();
        expect(await elementHandle.$$(`text/a`)).toHaveLength(0);
      });
    });
  });

  describe('XPath selectors', function () {
    describe('in Page', function () {
      it('should query existing element', async () => {
        const {page} = await getTestState();

        await page.setContent('<section>test</section>');

        expect(await page.$('xpath/html/body/section')).toBeTruthy();
        expect(await page.$$('xpath/html/body/section')).toHaveLength(1);
      });
      it('should return empty array for non-existing element', async () => {
        const {page} = await getTestState();

        expect(
          await page.$('xpath/html/body/non-existing-element'),
        ).toBeFalsy();
        expect(
          await page.$$('xpath/html/body/non-existing-element'),
        ).toHaveLength(0);
      });
      it('should return first element', async () => {
        const {page} = await getTestState();

        await page.setContent('<div>a</div><div></div>');

        using element = await page.$('xpath/html/body/div');
        expect(
          await element?.evaluate(e => {
            return e.textContent === 'a';
          }),
        ).toBeTruthy();
      });
      it('should return multiple elements', async () => {
        const {page} = await getTestState();

        await page.setContent('<div></div><div></div>');

        const elements = await page.$$('xpath/html/body/div');
        expect(elements).toHaveLength(2);
      });
    });
    describe('in ElementHandles', function () {
      it('should query existing element', async () => {
        const {page} = await getTestState();

        await page.setContent('<div class="a">a<span></span></div>');

        using elementHandle = (await page.$('div'))!;
        expect(await elementHandle.$(`xpath/span`)).toBeTruthy();
        expect(await elementHandle.$$(`xpath/span`)).toHaveLength(1);
      });

      it('should return null for non-existing element', async () => {
        const {page} = await getTestState();

        await page.setContent('<div class="a">a</div>');

        using elementHandle = (await page.$('div'))!;
        expect(await elementHandle.$(`xpath/span`)).toBeFalsy();
        expect(await elementHandle.$$(`xpath/span`)).toHaveLength(0);
      });
    });
  });

  describe('P selectors', () => {
    beforeEach(async () => {
      Puppeteer.clearCustomQueryHandlers();
    });

    it('should work with CSS selectors', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$('div > button');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();

      using root = await page.$('div');
      using button = await root!.$('& > button');
      assert(button, 'Could not find element');
      expect(
        await button.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();

      // Should parse more complex CSS selectors. Listing a few problematic
      // cases from bug reports.
      for (const selector of [
        '.user_row[data-user-id="\\38 "]:not(.deactivated_user)',
        `input[value='Search']:not([class='hidden'])`,
        `[data-test-id^="test-"]:not([data-test-id^="test-foo"])`,
        `& > table`,
      ]) {
        await page.$$(selector);
      }
    });

    it('should work with puppeteer pseudo classes', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$('button::-p-text(world)');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();
    });

    it('should work with deep combinators', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      {
        using element = await page.$('div >>>> div');
        assert(element, 'Could not find element');
        expect(
          await element.evaluate(element => {
            return element.id === 'c';
          }),
        ).toBeTruthy();
      }
      {
        const elements = await page.$$('div >>> div');
        assert(elements[1], 'Could not find element');
        expect(
          await elements[1]?.evaluate(element => {
            return element.id === 'd';
          }),
        ).toBeTruthy();
      }
      {
        const elements = await page.$$('#c >>>> div');
        assert(elements[0], 'Could not find element');
        expect(
          await elements[0]?.evaluate(element => {
            return element.id === 'd';
          }),
        ).toBeTruthy();
      }
      {
        const elements = await page.$$('#c >>> div');
        assert(elements[0], 'Could not find element');
        expect(
          await elements[0]?.evaluate(element => {
            return element.id === 'd';
          }),
        ).toBeTruthy();
      }
    });

    it('should work with text selectors', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$('div ::-p-text(world)');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();
    });

    it('should work ARIA selectors', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$('div ::-p-aria(world)');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();
    });

    it('should work for ARIA selectors in multiple isolated worlds', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.waitForSelector('::-p-aria(world)');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();
      // $ would add ARIA query handler to the main world.
      await element.$('::-p-aria(world)');
      using element2 = await page.waitForSelector('::-p-aria(world)');
      assert(element2, 'Could not find element');
      expect(
        await element2.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();
    });

    it('should work ARIA selectors with role', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$('::-p-aria(world[role="button"])');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();
    });

    it('should work ARIA selectors with name and role', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$('::-p-aria([name="world"][role="button"])');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();
    });

    it('should work XPath selectors', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$('div ::-p-xpath(//button)');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'b';
        }),
      ).toBeTruthy();
    });

    it('should work with custom selectors', async () => {
      Puppeteer.registerCustomQueryHandler('div', {
        queryOne() {
          return document.querySelector('div');
        },
      });

      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$('::-p-div');
      assert(element, 'Could not find element');
      expect(
        await element.evaluate(element => {
          return element.id === 'a';
        }),
      ).toBeTruthy();
    });

    it('should work with custom selectors with args', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      Puppeteer.registerCustomQueryHandler('div', {
        queryOne(_, selector) {
          if (selector === 'true') {
            return document.querySelector('div');
          } else {
            return document.querySelector('button');
          }
        },
      });

      {
        using element = await page.$('::-p-div(true)');
        assert(element, 'Could not find element');
        expect(
          await element.evaluate(element => {
            return element.id === 'a';
          }),
        ).toBeTruthy();
      }
      {
        using element = await page.$('::-p-div("true")');
        assert(element, 'Could not find element');
        expect(
          await element.evaluate(element => {
            return element.id === 'a';
          }),
        ).toBeTruthy();
      }
      {
        using element = await page.$("::-p-div('true')");
        assert(element, 'Could not find element');
        expect(
          await element.evaluate(element => {
            return element.id === 'a';
          }),
        ).toBeTruthy();
      }
      {
        using element = await page.$('::-p-div');
        assert(element, 'Could not find element');
        expect(
          await element.evaluate(element => {
            return element.id === 'b';
          }),
        ).toBeTruthy();
      }
    });

    it('should work with :hover', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using button = await page.$('div ::-p-text(world)');
      assert(button, 'Could not find element');
      await button.hover();

      using button2 = await page.$('div ::-p-text(world):hover');
      assert(button2, 'Could not find element');
      const value = await button2.evaluate(span => {
        return {textContent: span.textContent, tagName: span.tagName};
      });
      expect(value).toMatchObject({textContent: 'world', tagName: 'BUTTON'});
    });

    it('should work with selector lists', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      const elements = await page.$$('div, ::-p-text(world)');
      expect(elements).toHaveLength(3);
    });

    const permute = <T>(inputs: T[]): T[][] => {
      const results: T[][] = [];
      for (let i = 0; i < inputs.length; ++i) {
        const permutation = permute(
          inputs.slice(0, i).concat(inputs.slice(i + 1)),
        );
        const value = inputs[i] as T;
        if (permutation.length === 0) {
          results.push([value]);
          continue;
        }
        for (const part of permutation) {
          results.push([value].concat(part));
        }
      }
      return results;
    };

    it('should match querySelector* ordering', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      for (const list of permute(['div', 'button', 'span'])) {
        const elements = await page.$$(
          list
            .map(selector => {
              return selector === 'button' ? '::-p-text(world)' : selector;
            })
            .join(','),
        );
        const actual = await Promise.all(
          elements.map(element => {
            return element.evaluate(element => {
              return element.id;
            });
          }),
        );
        expect(actual.join()).toStrictEqual('a,b,f,c');
      }
    });

    it('should not have duplicate elements from selector lists', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      const elements = await page.$$('::-p-text(world), button');
      expect(elements).toHaveLength(1);
    });

    it('should handle escapes', async () => {
      const {server, page} = await getTestState();
      await page.goto(`${server.PREFIX}/p-selectors.html`);
      using element = await page.$(
        ':scope >>> ::-p-text(My name is Jun \\(pronounced like "June"\\))',
      );
      expect(element).toBeTruthy();
      using element2 = await page.$(
        ':scope >>> ::-p-text("My name is Jun (pronounced like \\"June\\")")',
      );
      expect(element2).toBeTruthy();
      using element3 = await page.$(
        ':scope >>> ::-p-text(My name is Jun \\(pronounced like "June"\\)")',
      );
      expect(element3).toBeFalsy();
      using element4 = await page.$(
        ':scope >>> ::-p-text("My name is Jun \\(pronounced like "June"\\))',
      );
      expect(element4).toBeFalsy();
    });
  });
});

[ Dauer der Verarbeitung: 0.4 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge