const CDP = require('chrome-remote-interface');
const axeCore = require('axe-core');
const assert = require('assert');
const { parse: parseURL } = require('url');

// Cheap URL validation
const isValidURL = input => {
  const u = parseURL(input);
  return u.protocol && u.host;
};

const example = async url => {
  // eslint-disable-next-line new-cap
  const client = await CDP();
  const { Runtime: runtime, Page: page } = client;

  let results;

  try {
    await page.enable();
    await runtime.enable();

    await page.navigate({ url });

    // This function is injected into the browser and is responsible for
    // running `axe-core`.
    const browserCode = () => {
      /* eslint-env browser */
      return new Promise((resolve, reject) => {
        const axe = window.axe;
        if (!axe) {
          throw new Error('Unable to find axe-core');
        }

        // Finally, run axe-core
        axe
          .run()
          // For some reason, when resolving with an object, CDP ignores
          // its value (`results.result.value` is undefined). By
          // `JSON.stringify()`ing it, we can `JSON.parse()` it later on
          // and return a valid results set.
          .then(res => JSON.stringify(res))
          .then(resolve)
          .catch(reject);
      });
    };

    // Inject axe-core
    await runtime.evaluate({
      expression: axeCore.source
    });

    // Run axe-core
    const ret = await runtime.evaluate({
      expression: `(${browserCode})()`,
      awaitPromise: true
    });

    // re-parse
    results = JSON.parse(ret.result.value);
  } catch (err) {
    // Ensure we close the client before exiting the fn
    client.close();
    throw err;
  }
  client.close();
  return results;
};

// node axe-cdp.js <url>
const url = process.argv[2];
assert(isValidURL(url), 'Invalid URL');

example(url)
  .then(results => {
    console.log(results);
  })
  .catch(err => {
    console.error('Error running axe-core:', err.message);
    process.exit(1);
  });
