{"version":3,"sources":["../src/index.ts","../src/json/json-reporter.ts","../src/html/html-reporter.ts","../src/html/data-serializer.ts","../src/html/image-handler.ts","../src/html/template-builder.ts"],"sourcesContent":["export { JsonReporter } from \"./json/json-reporter\";\nexport { HtmlReporter } from \"./html/html-reporter\";\nexport type {\n  ReporterOptions,\n  JsonReporterOptions,\n  HtmlReporterOptions,\n  ReportData,\n} from \"./types\";\n","/**\n * @fileoverview JSON reporter implementation for Visnap visual testing framework\n *\n * Generates JSON reports from test results with comprehensive test case details\n * and outcome statistics.\n */\n\nimport { writeFileSync, mkdirSync } from \"fs\";\nimport { dirname } from \"path\";\n\nimport type { TestResult } from \"@visnap/protocol\";\n\nimport type { JsonReporterOptions, ReportData } from \"../types\";\n\n/**\n * JSON reporter for generating test result reports in JSON format\n *\n * @example\n * ```typescript\n * const reporter = new JsonReporter();\n * const reportPath = await reporter.generate(testResult, {\n *   outputPath: \"./reports/test-results.json\",\n *   screenshotDir: \"./screenshots\"\n * });\n * ```\n */\nexport class JsonReporter {\n  /**\n   * Generates a JSON report from test results\n   * @param result - Test result data to include in the report\n   * @param options - Reporter options including output path and screenshot directory\n   * @returns Promise resolving to the absolute path of the generated report\n   * @throws {Error} If file writing fails\n   */\n  async generate(\n    result: TestResult,\n    options: JsonReporterOptions\n  ): Promise<string> {\n    const report: ReportData = {\n      success: result.success,\n      outcome: result.outcome,\n      failures: result.failures || [],\n      captureFailures: result.captureFailures || [],\n      config: result.config,\n      timestamp: new Date().toISOString(),\n    };\n\n    const reportJson = JSON.stringify(\n      report,\n      null,\n      options.pretty !== false ? 2 : undefined\n    );\n\n    const outputPath =\n      options.outputPath || `${options.screenshotDir}/report.json`;\n    const outputDir = dirname(outputPath);\n\n    mkdirSync(outputDir, { recursive: true });\n    writeFileSync(outputPath, reportJson);\n\n    return outputPath;\n  }\n}\n","/**\n * @fileoverview HTML reporter implementation for Visnap visual testing framework\n *\n * Generates interactive HTML reports from test results with visual diff comparison,\n * test case filtering, and comprehensive statistics.\n */\n\nimport { writeFileSync, mkdirSync } from \"fs\";\nimport { join, dirname } from \"path\";\n\nimport type { TestResult } from \"@visnap/protocol\";\n\nimport type { HtmlReporterOptions } from \"../types\";\n\nimport { serializeTestData } from \"./data-serializer\";\nimport { ImageHandler } from \"./image-handler\";\nimport { TemplateBuilder } from \"./template-builder\";\n\n/**\n * HTML reporter for generating interactive test result reports\n *\n * @example\n * ```typescript\n * const reporter = new HtmlReporter();\n * const reportPath = await reporter.generate(testResult, {\n *   outputPath: \"./reports/test-results.html\",\n *   screenshotDir: \"./screenshots\"\n * });\n * ```\n */\nexport class HtmlReporter {\n  /**\n   * Generates an HTML report from test results\n   * @param result - Test result data to include in the report\n   * @param options - Reporter options including output path and screenshot directory\n   * @returns Promise resolving to the absolute path of the generated report\n   * @throws {Error} If file writing fails\n   */\n  async generate(\n    result: TestResult,\n    options: HtmlReporterOptions\n  ): Promise<string> {\n    const data = serializeTestData(result);\n    const testCases = result.outcome.testCases || [];\n\n    // Process images\n    const imageHandler = new ImageHandler();\n    const processedTests = imageHandler.processTestCases(testCases);\n\n    // Build HTML\n    const templateBuilder = new TemplateBuilder();\n    const html = templateBuilder.build(data, processedTests, options.title);\n\n    // Determine output path\n    const outputPath =\n      options.outputPath || join(options.screenshotDir, \"report.html\");\n    const outputDir = dirname(outputPath);\n\n    mkdirSync(outputDir, { recursive: true });\n    writeFileSync(outputPath, html);\n\n    return outputPath;\n  }\n}\n","import type { TestResult, Viewport } from \"@visnap/protocol\";\n\nimport type { SerializedReportData } from \"../types\";\n\nexport function serializeTestData(result: TestResult): SerializedReportData {\n  const testCases = result.outcome.testCases || [];\n\n  const browsers = Array.from(\n    new Set(testCases.map(tc => tc.browser || \"N/A\"))\n  );\n  const viewports = Array.from(\n    new Set(\n      testCases.map(tc => {\n        if (!tc.viewport) return \"N/A\";\n        if (typeof tc.viewport === \"string\") return tc.viewport;\n        const width = (tc.viewport as Viewport).width ?? undefined;\n        const height = (tc.viewport as Viewport).height ?? undefined;\n        if (!width || !height) return \"N/A\";\n        return `${width}x${height}`;\n      })\n    )\n  );\n\n  const statusCounts = testCases.reduce(\n    (acc, tc) => {\n      acc[tc.status] = (acc[tc.status] || 0) + 1;\n      return acc;\n    },\n    {} as Record<string, number>\n  );\n\n  const groupedByStatus = testCases.reduce(\n    (acc, tc) => {\n      if (!acc[tc.status]) {\n        acc[tc.status] = [];\n      }\n      acc[tc.status].push(tc);\n      return acc;\n    },\n    {} as Record<string, typeof testCases>\n  );\n\n  return {\n    success: result.success,\n    outcome: result.outcome,\n    failures: result.failures || [],\n    captureFailures: result.captureFailures || [],\n    timestamp: new Date().toISOString(),\n    config: result.config,\n    duration: result.outcome.durations?.totalDurationMs,\n    testCases,\n    browsers,\n    viewports,\n    statusCounts,\n    groupedByStatus,\n  };\n}\n","import { join } from \"path\";\n\nimport type { TestCaseDetail } from \"@visnap/protocol\";\n\nimport type { ProcessedTestCase } from \"../types\";\n\nexport interface ProcessedImagePaths {\n  base: string;\n  current: string;\n  diff?: string;\n}\n\nexport class ImageHandler {\n  async getRelativeImagePaths(\n    testCase: TestCaseDetail,\n    _screenshotDir: string\n  ): Promise<ProcessedImagePaths> {\n    const baseImage = join(\"base\", testCase.captureFilename);\n    const currentImage = join(\"current\", testCase.captureFilename);\n    let diffImage: string | undefined;\n\n    if (testCase.status === \"failed\" && testCase.reason === \"pixel-diff\") {\n      diffImage = join(\"diff\", testCase.captureFilename);\n    }\n\n    return {\n      base: baseImage,\n      current: currentImage,\n      diff: diffImage,\n    };\n  }\n\n  processTestCases(testCases: TestCaseDetail[]): ProcessedTestCase[] {\n    return testCases.map(testCase => {\n      // Images are relative to the report location (screenshot directory)\n      // Report will be at: screenshotDir/report.html\n      // Images are at: screenshotDir/base/, screenshotDir/current/, screenshotDir/diff/\n\n      return {\n        ...testCase,\n        baseImage: `./base/${testCase.captureFilename}`,\n        currentImage: `./current/${testCase.captureFilename}`,\n        diffImage:\n          testCase.status === \"failed\" && testCase.reason === \"pixel-diff\"\n            ? `./diff/${testCase.captureFilename}`\n            : undefined,\n      };\n    });\n  }\n}\n","import { readFileSync } from \"fs\";\nimport { join, dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\n\nimport type { ReportData, ProcessedTestCase } from \"../types\";\n\nexport class TemplateBuilder {\n  private getAssetPath(filename: string): string {\n    // Always read from dist/assets folder (copied by tsup onSuccess)\n    // Use import.meta.url for ESM, fallback to require.resolve for CJS\n    try {\n      // ESM path resolution - the built file is in dist/, so go up one level to find assets\n      const currentFile = fileURLToPath(import.meta.url);\n      const currentDir = dirname(currentFile);\n      return join(currentDir, \"assets\", filename);\n    } catch {\n      // CJS path resolution\n      throw new Error(\"Failed to get asset path to generate HTML report\");\n    }\n  }\n\n  private templatePath = this.getAssetPath(\"template.html\");\n  private stylesPath = this.getAssetPath(\"styles.css\");\n  private scriptPath = this.getAssetPath(\"alpine-app.js\");\n\n  build(\n    data: ReportData,\n    testCases: ProcessedTestCase[],\n    title: string = \"VISNAP Test Report\"\n  ): string {\n    let template: string;\n    let styles: string;\n    let script: string;\n\n    try {\n      template = readFileSync(this.templatePath, \"utf-8\");\n      styles = readFileSync(this.stylesPath, \"utf-8\");\n      script = readFileSync(this.scriptPath, \"utf-8\");\n    } catch {\n      throw new Error(\"File not found\");\n    }\n\n    // Enrich data with processed test cases\n    const enrichedData = {\n      ...data,\n      outcome: {\n        ...data.outcome,\n        testCases,\n      },\n    };\n\n    return template\n      .replace(/{{TITLE}}/g, title)\n      .replace(\"{{STYLES}}\", styles)\n      .replace(\"{{DATA}}\", JSON.stringify(enrichedData))\n      .replace(\"{{SCRIPT}}\", script);\n  }\n}\n"],"mappings":"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","names":["import_fs","import_path","import_path","import_fs","import_path"]}