/*!
 * Custom elements for Nanopore-Digital Web applications
 */
// @ts-nocheck
import { newE2EPage } from "@stencil/core/testing";
import * as axe from "axe-core";
export class E2EPlayGround {
    props;
    viewportHeight;
    /** element template string. Add `{{ attrs }}` to place the attributes */
    template;
    cssVars;
    viewportWidth = 600;
    page;
    tag;
    constructor(setup) {
        setup =
            typeof setup === 'string'
                ? { template: `<${setup} {{ attrs }}></${setup}>`, tag: setup }
                : setup;
        this.template = setup.template;
        this.tag = setup.tag;
    }
    get attrs() {
        if (!this.props)
            return '';
        return Object.entries(this.props)
            .map(([attr, value]) => `${kebabize(attr)}="${value}"`)
            .join(' ');
    }
    get css() {
        if (!this.cssVars)
            return '';
        return Object.entries(this.cssVars)
            .map(([attr, value]) => `${attr}: ${value}`)
            .join('\n');
    }
    async newPage(props, docOpts) {
        if (!this.template || !this.tag) {
            console.warn('no template set');
            return;
        }
        this.props = props;
        this.page = await createPage({
            html: `
      <style>
        :root {
          ${this.css}
        }
      </style>
      ${this.template.replace(/(\{\{ attrs \}\})/g, this.attrs)}
    `,
            viewportWidth: this.viewportWidth,
            viewportHeight: this.viewportHeight,
            dir: docOpts ? docOpts.dir : 'ltr',
            padding: docOpts ? docOpts.padding : '1rem',
        });
        return this.page;
    }
    reportA11y() {
        if (!this.page) {
            console.warn('no page rendered');
            return;
        }
        return this.page.reportA11y(this.tag);
    }
    async component() {
        if (!this.page) {
            console.warn('no page rendered');
            return;
        }
        return this.page.find(this.tag);
    }
    async componentQuery(selectorStr, shadow = true) {
        if (!this.page) {
            console.warn('no page rendered');
            return;
        }
        if (!selectorStr) {
            console.warn('selectorStr not set');
            return;
        }
        if (shadow) {
            return (await this.page.waitForFunction((args) => {
                const [tag, selectorStr] = args;
                return document
                    .querySelector(tag)
                    .shadowRoot.querySelector(selectorStr);
            }, {}, [this.tag, selectorStr])).asElement();
        }
        else {
            return (await this.page.waitForFunction((args) => {
                const [tag, selectorStr] = args;
                return document.querySelector(tag + ' ' + selectorStr);
            }, {}, [this.tag, selectorStr])).asElement();
        }
    }
}
export async function getFocusedElement(page, shadowEleSelector = null) {
    if (!shadowEleSelector)
        return page.evaluateHandle(() => document.activeElement);
    else {
        return page.evaluateHandle((shadowEleSelector) => document.querySelector(shadowEleSelector).shadowRoot.activeElement, shadowEleSelector);
    }
}
export async function clickOnElement(page, elem, x = null, y = null) {
    const rect = await page.evaluate((el) => {
        const { top, left, width, height } = el.getBoundingClientRect();
        return { top, left, width, height };
    }, elem);
    // Use given position or default to center
    const _x = x !== null ? x : rect.width / 3;
    const _y = y !== null ? y : rect.height / 3;
    await page.mouse.click(rect.left + _x, rect.top + _y);
}
export async function createPage(optionsOrHtml) {
    const options = typeof optionsOrHtml === 'string'
        ? { html: optionsOrHtml, viewportWidth: 600, dir: 'ltr', padding: '1rem' }
        : optionsOrHtml;
    const page = (await newE2EPage());
    // setup navigator langs for consistent localization
    await page.evaluate(() => {
        Object.defineProperty(navigator, 'language', {
            get: function () {
                return 'en-GB';
            },
        });
        Object.defineProperty(navigator, 'languages', {
            get: function () {
                return ['en-GB'];
            },
        });
    });
    const viewport = {
        height: options.viewportHeight || page.viewport().height,
        width: options.viewportWidth,
    };
    // set viewport size
    await page.setViewport(viewport);
    // add content
    await page.setContent(`<link rel="stylesheet" href="/themes/nanopore.css">
    <div class="screenshot" style="padding: ${options.padding || '1rem'};">${options.html}</div>`, { waitUntil: 'networkidle2' });
    // set the direction - need to set here as stencil removes it on setContent
    await page.evaluate((dir) => {
        document.documentElement.setAttribute('dir', dir);
        document.dir = dir;
    }, options.dir);
    // monkey patch screenshot function to add some extra features
    const screenshot = page.screenshot;
    page.screenshot = async function () {
        // get the element's height, and set viewport to that height
        // this enables us to get full page, clipped screenshots
        const htmlElement = await page.$('.screenshot');
        const { width, height } = await htmlElement.boundingBox();
        await page.setViewport({
            width: page.viewport().width,
            height: options.viewportHeight || Math.round(height),
        });
        return screenshot.call(page, {
            clip: {
                x: 0,
                y: 0,
                width: Math.round(width),
                height: options.viewportHeight || Math.round(height),
            },
            captureBeyondViewport: false,
        });
    };
    await page.evaluate(async (source) => await window.eval(source), axe.source);
    page.reportA11y = async (contextQuery) => {
        const result = await page.evaluate(async (contextQuery) => {
            const context = {
                include: contextQuery ? [[contextQuery]] : undefined,
                exclude: [[contextQuery + ' .button--primary', '#shadow-host']],
            };
            const opts = {};
            return await window.axe.run(context, opts);
        }, contextQuery);
        // we only want serious issues and we want to ignore nanopore buttons. We know though they're bad.
        const violations = result.violations
            .filter((violation) => [`serious`, `critical`].includes(violation.impact))
            .filter((violation) => {
            violation.nodes = violation.nodes.filter((node) => !node.target.join().match('.button--'));
            return !!violation.nodes.length;
        });
        // const output = violations
        //   .map(
        //     (violations, index) => `(${index}) [Type: ${violations.id}]: ${
        //       violations.description
        //     } (${violations.help}).
        //       Elements: ${violations.nodes
        //         .map(
        //           (node, index) => `[${index}]'${node.target}'. HTML: ${node.html}`
        //         )
        //         .join(`, `)}.`
        //   )
        //   .join(`\n\n`);
        return violations.length ? JSON.stringify(violations, null, 2) : '';
        // return output;
    };
    return page;
}
export const kebabize = (str) => {
    return str
        .split('')
        .map((letter, idx) => {
        return letter.toUpperCase() === letter
            ? `${idx !== 0 ? '-' : ''}${letter.toLowerCase()}`
            : letter;
    })
        .join('');
};
export const screenshotSettings = (dirname) => {
    return {
        failureThreshold: 0.002,
        failureThresholdType: 'percent',
        customSnapshotsDir: `${dirname}/__screenshots__/${process.platform}`,
    };
};
export const waitForVisibleSnippet = (selector) => {
    return `
  (function () {
    function isVisible(elem) {
      if (!(elem instanceof Element)) throw Error('DomUtil: elem is not an element.');
      const style = getComputedStyle(elem);
      if (style.display === 'none') return false;
      if (style.visibility !== 'visible') return false;
      if (style.opacity < 0.1) return false;
      if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height +
          elem.getBoundingClientRect().width === 0) {
          return false;
      }
      const elemCenter   = {
          x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
          y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
      };
      if (elemCenter.x < 0) return false;
      if (elemCenter.x > (document.documentElement.clientWidth || window.innerWidth)) return false;
      if (elemCenter.y < 0) return false;
      if (elemCenter.y > (document.documentElement.clientHeight || window.innerHeight)) return false;
      let pointContainer = document.elementFromPoint(elemCenter.x, elemCenter.y);
      do {
          if (pointContainer === elem) return true;
      } while (pointContainer = pointContainer.parentNode);
      return false;
    }
    return isVisible(document.querySelector("${selector}"));
  })()
`;
};
export const stringifyFormSnippet = `
<div id="form-result"></div>
<script>
  var form = document.querySelector('form');
  var result = document.querySelector('#form-result');
  form.addEventListener('submit', (e) => {
    e.preventDefault();
    var data = new FormData(event.target);
    var object = {};
    data.forEach((value, key) => {
        // Reflect.has in favor of: object.hasOwnProperty(key)
        if(!Reflect.has(object, key)){
            object[key] = value;
            return;
        }
        if(!Array.isArray(object[key])){
            object[key] = [object[key]];
        }
        object[key].push(value);
    });
    result.innerHTML = JSON.stringify(object);
  })
</script>`;
