UNPKG

ui-coverage-scenario-tool-js

Version:

**UI Coverage Scenario Tool** is an innovative, no-overhead solution for tracking and visualizing UI test coverage — directly on your actual application, not static snapshots. The tool collects coverage during UI test execution and generates an interactiv

327 lines (314 loc) 10.6 kB
#!/usr/bin/env node // src/tools/actions.ts var ActionType = /* @__PURE__ */ ((ActionType2) => { ActionType2["Fill"] = "FILL"; ActionType2["Type"] = "TYPE"; ActionType2["Select"] = "SELECT"; ActionType2["Click"] = "CLICK"; ActionType2["Hover"] = "HOVER"; ActionType2["Text"] = "TEXT"; ActionType2["Value"] = "VALUE"; ActionType2["Hidden"] = "HIDDEN"; ActionType2["Visible"] = "VISIBLE"; ActionType2["Checked"] = "CHECKED"; ActionType2["Enabled"] = "ENABLED"; ActionType2["Disabled"] = "DISABLED"; ActionType2["Unchecked"] = "UNCHECKED"; return ActionType2; })(ActionType || {}); // src/tools/logger.ts var getLogger = (name) => ({ info: (msg) => console.info(`[${name}] ${msg}`), debug: (msg) => console.debug(`[${name}] ${msg}`), error: (msg) => console.error(`[${name}] ${msg}`), warning: (msg) => console.warn(`[${name}] ${msg}`) }); // src/tracker/storage.ts import fs2 from "fs/promises"; import path from "path"; import { v4 as uuidv4 } from "uuid"; // src/tools/files.ts import fs from "fs"; import yaml from "js-yaml"; import fsAsync from "fs/promises"; var logger = getLogger("FILES"); var isPathExists = async (path3) => { try { await fsAsync.access(path3); return true; } catch (error) { return false; } }; var loadFromJson = (file) => { try { if (!fs.existsSync(file)) return {}; const raw = fs.readFileSync(file, "utf-8"); return JSON.parse(raw); } catch (error) { logger.warning(`Failed to load JSON config ${file}: ${error}`); return {}; } }; var loadFromYaml = (file) => { try { if (!fs.existsSync(file)) return {}; const raw = fs.readFileSync(file, "utf-8"); return yaml.load(raw); } catch (error) { logger.warning(`Failed to load YAML config ${file}: ${error}`); return {}; } }; // src/tracker/models/pages.ts var CoveragePageResultList = class _CoveragePageResultList { constructor({ results }) { this.results = results; } filter({ app }) { const filtered = this.results.filter((result) => !app || result.app.toLowerCase() === app.toLowerCase()); return new _CoveragePageResultList({ results: filtered }); } unique() { const map = /* @__PURE__ */ new Map(); for (const result of this.results) { const key = result.page; if (!map.has(key)) { map.set(key, result); } } return new _CoveragePageResultList({ results: Array.from(map.values()) }); } findScenarios({ page }) { const scenarios = this.results.filter((result) => result.page === page).map((result) => result.scenario); return Array.from(new Set(scenarios)); } }; // src/tracker/models/elements.ts var CoverageElementResultList = class _CoverageElementResultList { constructor({ results }) { this.results = results; } filter({ app, scenario }) { const filtered = this.results.filter( (result) => (!app || result.app.toLowerCase() === app.toLowerCase()) && (!scenario || result.scenario.toLowerCase() === scenario.toLowerCase()) ); return new _CoverageElementResultList({ results: filtered }); } get groupedByAction() { return this.groupBy((r) => r.actionType); } get groupedBySelector() { return this.groupBy((r) => `${encodeURIComponent(r.selector)}|${r.selectorType}`); } get totalActions() { return this.results.length; } get totalSelectors() { return this.groupedBySelector.size; } countAction(actionType) { return this.results.filter((r) => r.actionType === actionType).length; } groupBy(keyGetter) { const map = /* @__PURE__ */ new Map(); for (const result of this.results) { const key = keyGetter(result); const results = map.get(key) || []; results.push(result); map.set(key, results); } const resultMap = /* @__PURE__ */ new Map(); for (const [key, results] of map.entries()) { resultMap.set(key, new _CoverageElementResultList({ results })); } return resultMap; } }; // src/tracker/models/scenarios.ts var CoverageScenarioResultList = class _CoverageScenarioResultList { constructor({ results }) { this.results = results; } filter({ app }) { const filtered = this.results.filter((result) => !app || result.app.toLowerCase() === app.toLowerCase()); return new _CoverageScenarioResultList({ results: filtered }); } }; // src/tracker/models/transitions.ts var CoverageTransitionResultList = class _CoverageTransitionResultList { constructor({ results }) { this.results = results; } filter({ app }) { const filtered = this.results.filter((result) => !app || result.app.toLowerCase() === app.toLowerCase()); return new _CoverageTransitionResultList({ results: filtered }); } unique() { const map = /* @__PURE__ */ new Map(); for (const result of this.results) { const key = `${result.fromPage}\u2192${result.toPage}`; if (!map.has(key)) { map.set(key, result); } } return new _CoverageTransitionResultList({ results: Array.from(map.values()) }); } findScenarios({ toPage, fromPage }) { const scenarios = this.results.filter((result) => result.toPage === toPage && result.fromPage === fromPage).map((result) => result.scenario); return Array.from(new Set(scenarios)); } countTransitions({ toPage, fromPage }) { return this.results.filter((result) => result.toPage === toPage && result.fromPage === fromPage).length; } }; // src/tracker/storage.ts var logger2 = getLogger("UI_COVERAGE_TRACKER_STORAGE"); var UICoverageTrackerStorage = class { constructor({ settings }) { this.settings = settings; } async load(props) { const { context, resultList } = props; const resultsDir = this.settings.resultsDir; logger2.info(`Loading coverage results from directory: ${resultsDir}`); if (!await isPathExists(resultsDir)) { logger2.warning(`Results directory does not exist: ${resultsDir}`); return new resultList({ results: [] }); } const results = []; for (const fileName of await fs2.readdir(resultsDir)) { const file = path.join(resultsDir, fileName); const fileStats = await fs2.stat(file); if (fileStats.isFile() && fileName.endsWith(`-${context}.json`)) { try { const json = await fs2.readFile(file, "utf-8"); results.push(JSON.parse(json)); } catch (error) { logger2.warning(`Failed to parse file ${fileName}: ${error}`); } } } logger2.info(`Loaded ${results.length} coverage files from directory: ${resultsDir}`); return new resultList({ results }); } async save({ result, context }) { const resultsDir = this.settings.resultsDir; if (!await isPathExists(resultsDir)) { logger2.info(`Results directory does not exist, creating: ${resultsDir}`); await fs2.mkdir(resultsDir, { recursive: true }); } const file = path.join(resultsDir, `${uuidv4()}-${context}.json`); try { await fs2.writeFile(file, JSON.stringify(result), "utf-8"); } catch (error) { logger2.error(`Error saving coverage data to file ${file}: ${error}`); } } async savePageResult(result) { await this.save({ context: "page", result }); } async saveElementResult(result) { await this.save({ context: "element", result }); } async saveScenarioResult(result) { await this.save({ context: "scenario", result }); } async saveTransitionResult(result) { await this.save({ context: "transition", result }); } async loadPageResults() { return await this.load({ context: "page", resultList: CoveragePageResultList }); } async loadElementResults() { return await this.load({ context: "element", resultList: CoverageElementResultList }); } async loadScenarioResults() { return await this.load({ context: "scenario", resultList: CoverageScenarioResultList }); } async loadTransitionResults() { return await this.load({ context: "transition", resultList: CoverageTransitionResultList }); } }; // src/tools/json.ts var logger3 = getLogger("JSON"); var loadJson = ({ content, fallback }) => { try { return JSON.parse(content, (key, value) => { switch (key) { case "createdAt": return new Date(value); default: return value; } }); } catch (error) { logger3.warning(`Failed to parse JSON: ${error}`); return fallback; } }; // src/config/builders.ts import path2 from "path"; import url from "url"; import dotenv from "dotenv"; dotenv.config(); var cwd = process.cwd(); var cleanUndefined = (input) => { return Object.fromEntries(Object.entries(input).filter(([_, v]) => v !== void 0)); }; var buildEnvSettings = () => { return cleanUndefined({ apps: loadJson({ content: process.env.UI_COVERAGE_SCENARIO_APPS || "", fallback: [] }), resultsDir: process.env.UI_COVERAGE_SCENARIO_RESULTS_DIR || void 0, historyFile: process.env.UI_COVERAGE_SCENARIO_HISTORY_FILE || void 0, historyRetentionLimit: parseInt(process.env.UI_COVERAGE_SCENARIO_HISTORY_RETENTION_LIMIT || "", 10) || void 0, htmlReportFile: process.env.UI_COVERAGE_SCENARIO_HTML_REPORT_FILE || void 0, jsonReportFile: process.env.UI_COVERAGE_SCENARIO_JSON_REPORT_FILE || void 0 }); }; var buildJsonSettings = () => { return cleanUndefined(loadFromJson(path2.join(cwd, "ui-coverage-scenario.config.json"))); }; var buildYamlSettings = () => { return cleanUndefined(loadFromYaml(path2.join(cwd, "ui-coverage-scenario.config.yaml"))); }; var buildDefaultSettings = () => { let htmlReportTemplateFile; try { htmlReportTemplateFile = path2.join( path2.dirname(url.fileURLToPath(import.meta.url)), "reports/templates/index.html" ); } catch { htmlReportTemplateFile = path2.join(cwd, "src/reports/templates/index.html"); } return { apps: [], resultsDir: path2.join(cwd, "coverage-results"), historyFile: path2.join(cwd, "coverage-history.json"), historyRetentionLimit: 30, htmlReportFile: path2.join(cwd, "index.html"), jsonReportFile: path2.join(cwd, "coverage-report.json"), htmlReportTemplateFile }; }; // src/config/core.ts var getSettings = () => { const defaultSettings = buildDefaultSettings(); return { ...defaultSettings, ...buildYamlSettings(), ...buildJsonSettings(), ...buildEnvSettings(), htmlReportTemplateFile: defaultSettings.htmlReportTemplateFile }; }; export { ActionType, getLogger, isPathExists, UICoverageTrackerStorage, loadJson, getSettings }; //# sourceMappingURL=chunk-KWTIKUCY.js.map