// src/aria-snapshot.ts
import { writeFile } from "fs/promises";
import { getMetadataPath, writeMetadata } from "@argos-ci/util";

// src/util.ts
import { createRequire as createRequire2 } from "module";

// src/metadata.ts
import {
  getGitRepositoryPath,
  readVersionFromPackage
} from "@argos-ci/util";
import { relative } from "path";
import { createRequire } from "module";
import { AsyncLocalStorage } from "async_hooks";
var require2 = createRequire(import.meta.url);
function tryResolve(pkg) {
  try {
    return require2.resolve(pkg);
  } catch {
    return null;
  }
}
var metadataConfigStorage = new AsyncLocalStorage();
function setMetadataConfig(metadata) {
  metadataConfigStorage.enterWith(metadata);
}
var DEFAULT_PLAYWRIGHT_LIBRARIES = [
  "@playwright/test",
  "playwright",
  "playwright-core"
];
function getMetadataOverrides() {
  return metadataConfigStorage.getStore();
}
async function getAutomationLibraryMetadata() {
  const metadataConfig = metadataConfigStorage.getStore();
  const libraries = metadataConfig?.playwrightLibraries ?? DEFAULT_PLAYWRIGHT_LIBRARIES;
  for (const name of libraries) {
    const pkgPath = tryResolve(`${name}/package.json`);
    if (pkgPath) {
      const version = await readVersionFromPackage(pkgPath);
      return { version, name };
    }
  }
  throw new Error(
    `Unable to find any of the following packages: ${libraries.join(", ")}`
  );
}
async function getArgosPlaywrightVersion() {
  const pkgPath = require2.resolve("@argos-ci/playwright/package.json");
  return readVersionFromPackage(pkgPath);
}
async function getSdkMetadata() {
  const metadataConfig = metadataConfigStorage.getStore();
  if (metadataConfig) {
    return metadataConfig.sdk;
  }
  const argosPlaywrightVersion = await getArgosPlaywrightVersion();
  return {
    name: "@argos-ci/playwright",
    version: argosPlaywrightVersion
  };
}
async function getLibraryMetadata() {
  const [automationLibrary, sdk] = await Promise.all([
    getAutomationLibraryMetadata(),
    getSdkMetadata()
  ]);
  return {
    automationLibrary,
    sdk
  };
}
function resolveTestFilePath(filepath, repositoryPath) {
  if (!repositoryPath) {
    return filepath;
  }
  return relative(repositoryPath, filepath);
}
async function getTestMetadata(testInfo) {
  const repositoryPath = await getGitRepositoryPath();
  const metadataConfig = metadataConfigStorage.getStore();
  if (metadataConfig?.test) {
    return {
      ...metadataConfig.test,
      location: metadataConfig.test?.location ? {
        file: resolveTestFilePath(
          metadataConfig.test.location.file,
          repositoryPath
        ),
        line: metadataConfig.test.location.line,
        column: metadataConfig.test.location.column
      } : void 0
    };
  }
  if (!testInfo) {
    return null;
  }
  const testMetadata = {
    id: testInfo.testId,
    title: testInfo.title,
    titlePath: testInfo.titlePath,
    retry: testInfo.retry,
    retries: testInfo.project.retries,
    repeat: testInfo.repeatEachIndex,
    location: {
      file: resolveTestFilePath(testInfo.file, repositoryPath),
      line: testInfo.line,
      column: testInfo.column
    },
    annotations: testInfo.annotations
  };
  return testMetadata;
}

// src/util.ts
import {
  getGlobalScript
} from "@argos-ci/browser";
import { dirname, resolve } from "path";
import { mkdir } from "fs/promises";
var require3 = createRequire2(import.meta.url);
function checkIsUsingArgosReporter(testInfo) {
  if (!testInfo) {
    return false;
  }
  const reporterPath = require3.resolve("@argos-ci/playwright/reporter");
  return testInfo.config.reporter.some(
    (reporter) => reporter[0].includes("@argos-ci/playwright/reporter") || reporter[0] === reporterPath
  );
}
var PNG_EXTENSION = `.png`;
var ARIA_EXTENSION = `.aria.yml`;
var METADATA_EXTENSION = `.argos.json`;
var MAX_NAME_LENGTH = 255 - PNG_EXTENSION.length - METADATA_EXTENSION.length;
async function getTestInfo() {
  try {
    const { test } = await import("@playwright/test");
    return test.info();
  } catch {
    return null;
  }
}
function checkIsPage(value) {
  return Boolean(
    value && typeof value === "object" && "bringToFront" in value && typeof value.bringToFront === "function"
  );
}
function checkIsElementHandle(value) {
  return Boolean(
    value && typeof value === "object" && "asElement" in value && typeof value.asElement === "function"
  );
}
function checkIsFrame(handler) {
  return "page" in handler && typeof handler.page === "function";
}
function getPage(handler) {
  if (checkIsFrame(handler)) {
    return handler.page();
  }
  return handler;
}
function getViewportSize(page) {
  const viewportSize = page.viewportSize();
  if (!viewportSize) {
    throw new Error("Snapshots can't be taken without a viewport.");
  }
  return viewportSize;
}
async function setViewportSize(page, viewportSize) {
  await page.setViewportSize(viewportSize);
  await page.waitForFunction(
    ({ width, height }) => window.innerWidth === width && window.innerHeight === height,
    { width: viewportSize.width, height: viewportSize.height }
  );
}
function getSnapshotNames(name, testInfo) {
  if (testInfo) {
    const projectName = `${testInfo.project.name}/${name}`;
    if (testInfo.repeatEachIndex > 0) {
      return {
        name: `${projectName} repeat-${testInfo.repeatEachIndex}`,
        baseName: projectName
      };
    }
    return { name: projectName, baseName: null };
  }
  return { name, baseName: null };
}
async function injectArgos(handler) {
  const injected = await handler.evaluate(
    () => typeof window.__ARGOS__ !== "undefined"
  );
  if (!injected) {
    await handler.addScriptTag({ content: getGlobalScript() });
  }
}
async function prepare(args) {
  const { handler, useArgosReporter, root } = args;
  await Promise.all([
    // Create the screenshot folder if it doesn't exist
    useArgosReporter ? null : mkdir(root, { recursive: true }),
    // Inject Argos script into the page
    injectArgos(handler)
  ]);
}
async function getPathAndMetadata(args) {
  const { handler, testInfo, names, extension, root, useArgosReporter } = args;
  const overrides = getMetadataOverrides();
  const path = useArgosReporter && testInfo ? testInfo.outputPath("argos", `${names.name}${extension}`) : resolve(root, `${names.name}${extension}`);
  const dir = dirname(path);
  const [colorScheme, mediaType, libMetadata, testMetadata] = await Promise.all(
    [
      handler.evaluate(
        () => window.__ARGOS__.getColorScheme()
      ),
      handler.evaluate(
        () => window.__ARGOS__.getMediaType()
      ),
      getLibraryMetadata(),
      getTestMetadata(testInfo),
      dir !== root ? mkdir(dir, { recursive: true }) : null
    ]
  );
  const viewportSize = checkIsFrame(handler) ? null : getViewportSize(handler);
  const browser = getPage(handler).context().browser();
  if (!browser) {
    throw new Error("Can't take screenshots without a browser.");
  }
  const browserName = browser.browserType().name();
  const browserVersion = browser.version();
  const url = overrides?.url ?? handler.url();
  const metadata = {
    url,
    colorScheme,
    mediaType,
    test: testMetadata,
    browser: {
      name: browserName,
      version: browserVersion
    },
    ...libMetadata
  };
  const viewport = viewportSize ?? getMetadataOverrides()?.viewport;
  if (viewport) {
    metadata.viewport = viewport;
  }
  metadata.transient = {};
  if (names.baseName) {
    metadata.transient.baseName = `${names.baseName}${extension}`;
  }
  return {
    metadata,
    path
  };
}
function screenshotToSnapshotPath(value) {
  return value.replace(/\.png$/, ARIA_EXTENSION);
}
async function beforeAll(handler, context, options) {
  await handler.evaluate(
    (context2) => window.__ARGOS__.beforeAll(context2),
    context
  );
  if (options?.disableHover) {
    await getPage(handler).mouse.move(0, 0);
  }
  return async () => {
    await handler.evaluate(
      () => window.__ARGOS__.afterAll()
    );
  };
}
async function beforeEach(handler, context) {
  await handler.evaluate(
    (context2) => window.__ARGOS__.beforeEach(context2),
    context
  );
  return async () => {
    await handler.evaluate(
      () => window.__ARGOS__.afterEach()
    );
  };
}
async function increaseTimeout() {
  const testInfo = await getTestInfo();
  if (testInfo) {
    const { timeout } = testInfo;
    testInfo.setTimeout(timeout * 3);
    return {
      value: timeout,
      reset: () => {
        testInfo.setTimeout(timeout);
      }
    };
  }
  return null;
}
async function waitForReadiness(handler, context) {
  const timeout = await increaseTimeout();
  try {
    await handler.waitForFunction(
      (context2) => {
        const argos = window.__ARGOS__;
        return argos.waitFor(context2);
      },
      context,
      timeout ? { timeout: timeout.value } : void 0
    );
    timeout?.reset();
  } catch (error) {
    const reasons = await handler.evaluate(
      (context2) => window.__ARGOS__.getWaitFailureExplanations(
        context2
      ),
      context
    );
    throw new Error(
      `
Failed to stabilize screenshot, found the following issues:
${reasons.map((reason) => `- ${reason}`).join("\n")}
        `.trim(),
      { cause: error }
    );
  }
}
async function attachAttachments(args) {
  const { attachments, useArgosReporter, testInfo } = args;
  if (useArgosReporter && testInfo) {
    await Promise.all(
      attachments.map(
        (attachment) => testInfo.attach(attachment.name, {
          path: attachment.path,
          contentType: attachment.contentType
        })
      )
    );
  }
}

// src/attachment.ts
function getAttachmentName(name, type) {
  return `argos/${type}___${name}`;
}

// src/aria-snapshot.ts
var DEFAULT_SNAPSHOTS_ROOT = "./screenshots";
async function argosAriaSnapshot(handler, name, options = {}) {
  const {
    element,
    has,
    hasText,
    hasNot,
    hasNotText,
    timeout,
    root = DEFAULT_SNAPSHOTS_ROOT
  } = options;
  if (!handler) {
    throw new Error("A Playwright `handler` object is required.");
  }
  if (!name) {
    throw new Error("The `name` argument is required.");
  }
  const snapshotTarget = typeof element === "string" ? handler.locator(element, { has, hasText, hasNot, hasNotText }) : element ?? handler.locator("body");
  const testInfo = await getTestInfo();
  const useArgosReporter = checkIsUsingArgosReporter(testInfo);
  await prepare({ handler, useArgosReporter, root });
  const context = getStabilizationContext(options);
  const afterAll = await beforeAll(handler, context);
  const names = getSnapshotNames(name, testInfo);
  const { path: snapshotPath, metadata } = await getPathAndMetadata({
    handler,
    extension: ARIA_EXTENSION,
    names,
    root,
    testInfo,
    useArgosReporter
  });
  await waitForReadiness(handler, context);
  const afterEach = await beforeEach(handler, context);
  await waitForReadiness(handler, context);
  await Promise.all([
    snapshotTarget.ariaSnapshot({ timeout }).then((snapshot) => {
      return writeFile(snapshotPath, snapshot, "utf-8");
    }),
    writeMetadata(snapshotPath, metadata)
  ]);
  const attachments = [
    {
      name: getAttachmentName(names.name, "aria"),
      contentType: "application/yaml",
      path: snapshotPath
    },
    {
      name: getAttachmentName(names.name, "aria/metadata"),
      contentType: "application/json",
      path: getMetadataPath(snapshotPath)
    }
  ];
  await attachAttachments({ attachments, testInfo, useArgosReporter });
  await afterEach();
  await afterAll();
  return attachments;
}
function getStabilizationContext(options) {
  const { stabilize } = options;
  return {
    fullPage: false,
    argosCSS: void 0,
    viewports: void 0,
    options: stabilize
  };
}

// src/screenshot.ts
import {
  resolveViewport
} from "@argos-ci/browser";
import {
  getMetadataPath as getMetadataPath2,
  getScreenshotName,
  validateThreshold,
  writeMetadata as writeMetadata2
} from "@argos-ci/util";
import { writeFile as writeFile2 } from "fs/promises";
var DEFAULT_SCREENSHOT_ROOT = "./screenshots";
async function argosScreenshot(handler, name, options = {}) {
  const {
    element,
    has,
    hasText,
    hasNot,
    hasNotText,
    viewports,
    argosCSS: _argosCSS,
    root = DEFAULT_SCREENSHOT_ROOT,
    ariaSnapshot,
    disableHover = true,
    ...playwrightOptions
  } = options;
  if (!handler) {
    throw new Error("A Playwright `handler` object is required.");
  }
  if (!name) {
    throw new Error("The `name` argument is required.");
  }
  const screenshotTarget = typeof element === "string" ? handler.locator(element, { has, hasText, hasNot, hasNotText }) : element ?? (checkIsFrame(handler) ? handler.locator("body") : handler);
  const testInfo = await getTestInfo();
  const useArgosReporter = checkIsUsingArgosReporter(testInfo);
  await prepare({ handler, useArgosReporter, root });
  const originalViewportSize = checkIsFrame(handler) ? null : getViewportSize(handler);
  const fullPage = options.fullPage !== void 0 ? options.fullPage : screenshotTarget === handler;
  const context = getStabilizationContext2(options);
  const afterAll = await beforeAll(handler, context, { disableHover });
  const stabilizeAndScreenshot = async (name2) => {
    const names = getSnapshotNames(name2, testInfo);
    const { path: screenshotPath, metadata } = await getPathAndMetadata({
      handler,
      extension: PNG_EXTENSION,
      root,
      names,
      testInfo,
      useArgosReporter
    });
    if (options.threshold !== void 0) {
      validateThreshold(options.threshold);
      metadata.transient.threshold = options.threshold;
    }
    await options.beforeScreenshot?.({
      runStabilization: (stabilizationOptions) => waitForReadiness(
        handler,
        getStabilizationContext2({
          ...options,
          stabilize: stabilizationOptions ?? options.stabilize
        })
      )
    });
    await waitForReadiness(handler, context);
    const afterEach = await beforeEach(handler, context);
    await waitForReadiness(handler, context);
    const [snapshotPath] = await Promise.all([
      (async () => {
        if (!ariaSnapshot) {
          return null;
        }
        const snapshotTarget = checkIsPage(screenshotTarget) ? screenshotTarget.locator("body") : screenshotTarget;
        if (checkIsElementHandle(snapshotTarget)) {
          throw new Error(
            `Element handle is not supported with "ariaSnapshot" option. Use a Locator instead.`
          );
        }
        const snapshotPath2 = screenshotToSnapshotPath(screenshotPath);
        const snapshotMetadata = {
          ...metadata,
          transient: {
            parentName: `${names.name}${PNG_EXTENSION}`,
            ...metadata.transient.baseName ? {
              baseName: screenshotToSnapshotPath(
                metadata.transient.baseName
              )
            } : {}
          }
        };
        await Promise.all([
          snapshotTarget.ariaSnapshot().then((snapshot) => {
            return writeFile2(snapshotPath2, snapshot, "utf-8");
          }),
          writeMetadata2(snapshotPath2, snapshotMetadata)
        ]);
        return snapshotPath2;
      })(),
      screenshotTarget.screenshot({
        path: screenshotPath,
        type: "png",
        fullPage,
        mask: [handler.locator('[data-visual-test="blackout"]')],
        animations: "disabled",
        ...playwrightOptions
      }),
      writeMetadata2(screenshotPath, metadata)
    ]);
    const attachments = [
      {
        name: getAttachmentName(names.name, "screenshot"),
        contentType: "image/png",
        path: screenshotPath
      },
      {
        name: getAttachmentName(names.name, "screenshot/metadata"),
        contentType: "application/json",
        path: getMetadataPath2(screenshotPath)
      }
    ];
    if (snapshotPath) {
      attachments.push(
        {
          name: getAttachmentName(names.name, "aria"),
          contentType: "application/yaml",
          path: snapshotPath
        },
        {
          name: getAttachmentName(names.name, "aria/metadata"),
          contentType: "application/json",
          path: getMetadataPath2(snapshotPath)
        }
      );
    }
    await attachAttachments({ attachments, testInfo, useArgosReporter });
    await afterEach();
    await options.afterScreenshot?.();
    return attachments;
  };
  const allAttachments = [];
  if (viewports) {
    if (checkIsFrame(handler)) {
      throw new Error(`viewports option is not supported with an iframe`);
    }
    for (const viewport of viewports) {
      const viewportSize = resolveViewport(viewport);
      await setViewportSize(handler, viewportSize);
      const attachments = await stabilizeAndScreenshot(
        getScreenshotName(name, { viewportWidth: viewportSize.width })
      );
      allAttachments.push(...attachments);
    }
    if (!originalViewportSize) {
      throw new Error(`Invariant: viewport size must be saved`);
    }
    await setViewportSize(handler, originalViewportSize);
  } else {
    const attachments = await stabilizeAndScreenshot(name);
    allAttachments.push(...attachments);
  }
  await afterAll();
  return allAttachments;
}
function getStabilizationContext2(options) {
  const { fullPage, argosCSS, stabilize, viewports } = options;
  return {
    fullPage,
    argosCSS,
    viewports,
    options: stabilize
  };
}

// src/csp.ts
import { getGlobalScript as getGlobalScript2 } from "@argos-ci/browser";
import { createHash } from "crypto";
function getCSPScriptHash() {
  const hash = createHash("sha256").update(getGlobalScript2()).digest("base64");
  return `'sha256-${hash}'`;
}
export {
  setMetadataConfig as DO_NOT_USE_setMetadataConfig,
  argosAriaSnapshot,
  argosScreenshot,
  getCSPScriptHash
};
