qat-cli 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +203 -77
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**Quick Auto Testing — 面向 Vue 项目,集成 Vitest & Playwright,AI 驱动覆盖测试全流程**
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/qat-cli)
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
[](https://www.typescriptlang.org/)
|
package/dist/cli.js
CHANGED
|
@@ -3169,6 +3169,8 @@ function displayCreateResult(createdFiles, skippedCount, usedAI) {
|
|
|
3169
3169
|
import chalk5 from "chalk";
|
|
3170
3170
|
import inquirer3 from "inquirer";
|
|
3171
3171
|
import ora4 from "ora";
|
|
3172
|
+
import fs9 from "fs";
|
|
3173
|
+
import path11 from "path";
|
|
3172
3174
|
|
|
3173
3175
|
// src/runners/vitest-runner.ts
|
|
3174
3176
|
import { execFile } from "child_process";
|
|
@@ -3834,6 +3836,8 @@ function calculateAverageMetrics(results) {
|
|
|
3834
3836
|
}
|
|
3835
3837
|
|
|
3836
3838
|
// src/commands/run.ts
|
|
3839
|
+
var RESULTS_DIR = ".qat-results";
|
|
3840
|
+
var SERVER_REQUIRED_TYPES = ["e2e", "visual", "performance"];
|
|
3837
3841
|
var TYPE_RUNNERS = {
|
|
3838
3842
|
unit: "Vitest",
|
|
3839
3843
|
component: "Vitest",
|
|
@@ -3878,6 +3882,62 @@ async function executeRun(options) {
|
|
|
3878
3882
|
console.log(chalk5.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\uFF08\u8BF7\u5728 qat.config.ts \u4E2D\u542F\u7528\uFF09\n"));
|
|
3879
3883
|
return;
|
|
3880
3884
|
}
|
|
3885
|
+
const serverNeededTypes = typesToRun.filter((t) => SERVER_REQUIRED_TYPES.includes(t));
|
|
3886
|
+
if (serverNeededTypes.length > 0) {
|
|
3887
|
+
const serverOk = await checkDevServer(config);
|
|
3888
|
+
if (!serverOk) {
|
|
3889
|
+
const { action } = await inquirer3.prompt([
|
|
3890
|
+
{
|
|
3891
|
+
type: "list",
|
|
3892
|
+
name: "action",
|
|
3893
|
+
message: `\u8FD0\u884C ${serverNeededTypes.map((t) => TYPE_LABELS[t]).join("\u3001")} \u9700\u8981\u9879\u76EE\u670D\u52A1\u8FD0\u884C\uFF0C\u5982\u4F55\u5904\u7406\uFF1F`,
|
|
3894
|
+
choices: [
|
|
3895
|
+
{ name: "\u81EA\u52A8\u542F\u52A8 dev server \u5E76\u8FD0\u884C", value: "start" },
|
|
3896
|
+
{ name: "\u8DF3\u8FC7\u8FD9\u4E9B\u6D4B\u8BD5\u7C7B\u578B\uFF0C\u4EC5\u8FD0\u884C\u5176\u4ED6\u6D4B\u8BD5", value: "skip" },
|
|
3897
|
+
{ name: "\u53D6\u6D88\u8FD0\u884C", value: "cancel" }
|
|
3898
|
+
],
|
|
3899
|
+
default: "start"
|
|
3900
|
+
}
|
|
3901
|
+
]);
|
|
3902
|
+
if (action === "cancel") {
|
|
3903
|
+
console.log(chalk5.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
|
|
3904
|
+
return;
|
|
3905
|
+
}
|
|
3906
|
+
if (action === "skip") {
|
|
3907
|
+
typesToRun = typesToRun.filter((t) => !SERVER_REQUIRED_TYPES.includes(t));
|
|
3908
|
+
if (typesToRun.length === 0) {
|
|
3909
|
+
console.log(chalk5.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
|
|
3910
|
+
return;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
if (action === "start") {
|
|
3914
|
+
const started = await startDevServer(config);
|
|
3915
|
+
if (!started) {
|
|
3916
|
+
const { fallback } = await inquirer3.prompt([
|
|
3917
|
+
{
|
|
3918
|
+
type: "list",
|
|
3919
|
+
name: "fallback",
|
|
3920
|
+
message: "dev server \u542F\u52A8\u5931\u8D25\uFF0C\u5982\u4F55\u5904\u7406\uFF1F",
|
|
3921
|
+
choices: [
|
|
3922
|
+
{ name: "\u8DF3\u8FC7\u9700\u8981\u670D\u52A1\u7684\u6D4B\u8BD5\uFF0C\u4EC5\u8FD0\u884C\u5176\u4ED6\u6D4B\u8BD5", value: "skip" },
|
|
3923
|
+
{ name: "\u53D6\u6D88\u8FD0\u884C", value: "cancel" }
|
|
3924
|
+
],
|
|
3925
|
+
default: "skip"
|
|
3926
|
+
}
|
|
3927
|
+
]);
|
|
3928
|
+
if (fallback === "cancel") {
|
|
3929
|
+
console.log(chalk5.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
|
|
3930
|
+
return;
|
|
3931
|
+
}
|
|
3932
|
+
typesToRun = typesToRun.filter((t) => !SERVER_REQUIRED_TYPES.includes(t));
|
|
3933
|
+
if (typesToRun.length === 0) {
|
|
3934
|
+
console.log(chalk5.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
|
|
3935
|
+
return;
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3881
3941
|
if (!type && !file) {
|
|
3882
3942
|
const { runMode } = await inquirer3.prompt([
|
|
3883
3943
|
{
|
|
@@ -3924,6 +3984,7 @@ async function executeRun(options) {
|
|
|
3924
3984
|
}
|
|
3925
3985
|
}
|
|
3926
3986
|
displaySummary(results, config);
|
|
3987
|
+
saveRunResults(results);
|
|
3927
3988
|
}
|
|
3928
3989
|
function printDryRunCommands(types, options, config) {
|
|
3929
3990
|
console.log();
|
|
@@ -4251,6 +4312,71 @@ function scoreColor(score) {
|
|
|
4251
4312
|
if (score >= 50) return chalk5.yellow("\u25CF");
|
|
4252
4313
|
return chalk5.red("\u25CF");
|
|
4253
4314
|
}
|
|
4315
|
+
async function checkDevServer(config) {
|
|
4316
|
+
const baseUrl = config.playwright.baseURL || "http://localhost:5173";
|
|
4317
|
+
try {
|
|
4318
|
+
const controller = new AbortController();
|
|
4319
|
+
const timer = setTimeout(() => controller.abort(), 3e3);
|
|
4320
|
+
const response = await fetch(baseUrl, { signal: controller.signal, method: "HEAD" });
|
|
4321
|
+
clearTimeout(timer);
|
|
4322
|
+
return response.status > 0;
|
|
4323
|
+
} catch {
|
|
4324
|
+
return false;
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
async function startDevServer(config) {
|
|
4328
|
+
const { spawn } = await import("child_process");
|
|
4329
|
+
const hasPnpm = fs9.existsSync(path11.join(process.cwd(), "pnpm-lock.yaml"));
|
|
4330
|
+
const hasYarn = fs9.existsSync(path11.join(process.cwd(), "yarn.lock"));
|
|
4331
|
+
const pkgCmd = hasPnpm ? "pnpm" : hasYarn ? "yarn" : "npm";
|
|
4332
|
+
console.log(chalk5.cyan(` \u6B63\u5728\u542F\u52A8 dev server (${pkgCmd} run dev) ...`));
|
|
4333
|
+
const child = spawn(pkgCmd, ["run", "dev"], {
|
|
4334
|
+
cwd: process.cwd(),
|
|
4335
|
+
stdio: "pipe",
|
|
4336
|
+
shell: true,
|
|
4337
|
+
detached: true
|
|
4338
|
+
});
|
|
4339
|
+
const baseUrl = config.playwright.baseURL || "http://localhost:5173";
|
|
4340
|
+
const maxWait = 3e4;
|
|
4341
|
+
const interval = 1e3;
|
|
4342
|
+
let waited = 0;
|
|
4343
|
+
while (waited < maxWait) {
|
|
4344
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
4345
|
+
waited += interval;
|
|
4346
|
+
const isUp = await checkDevServer(config);
|
|
4347
|
+
if (isUp) {
|
|
4348
|
+
console.log(chalk5.green(` \u2713 dev server \u5DF2\u542F\u52A8 (${baseUrl})`));
|
|
4349
|
+
child.unref();
|
|
4350
|
+
return true;
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
child.kill();
|
|
4354
|
+
console.log(chalk5.red(" \u2717 dev server \u542F\u52A8\u8D85\u65F6"));
|
|
4355
|
+
return false;
|
|
4356
|
+
}
|
|
4357
|
+
function saveRunResults(results) {
|
|
4358
|
+
if (results.length === 0) return;
|
|
4359
|
+
const resultsPath = path11.join(process.cwd(), RESULTS_DIR);
|
|
4360
|
+
if (!fs9.existsSync(resultsPath)) {
|
|
4361
|
+
fs9.mkdirSync(resultsPath, { recursive: true });
|
|
4362
|
+
}
|
|
4363
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4364
|
+
const fileName = `result-${timestamp}.json`;
|
|
4365
|
+
const filePath = path11.join(resultsPath, fileName);
|
|
4366
|
+
const data = {
|
|
4367
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4368
|
+
results
|
|
4369
|
+
};
|
|
4370
|
+
fs9.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
4371
|
+
try {
|
|
4372
|
+
const files = fs9.readdirSync(resultsPath).filter((f) => f.startsWith("result-") && f.endsWith(".json")).sort();
|
|
4373
|
+
while (files.length > 20) {
|
|
4374
|
+
const oldest = files.shift();
|
|
4375
|
+
fs9.unlinkSync(path11.join(resultsPath, oldest));
|
|
4376
|
+
}
|
|
4377
|
+
} catch {
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
4254
4380
|
|
|
4255
4381
|
// src/commands/mock.ts
|
|
4256
4382
|
import chalk6 from "chalk";
|
|
@@ -4374,12 +4500,12 @@ function showStatus() {
|
|
|
4374
4500
|
// src/commands/report.ts
|
|
4375
4501
|
import chalk7 from "chalk";
|
|
4376
4502
|
import ora6 from "ora";
|
|
4377
|
-
import
|
|
4378
|
-
import
|
|
4503
|
+
import fs11 from "fs";
|
|
4504
|
+
import path13 from "path";
|
|
4379
4505
|
|
|
4380
4506
|
// src/services/reporter.ts
|
|
4381
|
-
import
|
|
4382
|
-
import
|
|
4507
|
+
import fs10 from "fs";
|
|
4508
|
+
import path12 from "path";
|
|
4383
4509
|
function aggregateResults(results) {
|
|
4384
4510
|
const summary = {
|
|
4385
4511
|
total: 0,
|
|
@@ -4634,17 +4760,17 @@ function generateHTMLReport(data) {
|
|
|
4634
4760
|
}
|
|
4635
4761
|
function writeReportToDisk(data, outputDir) {
|
|
4636
4762
|
const html = generateHTMLReport(data);
|
|
4637
|
-
const dir =
|
|
4638
|
-
if (!
|
|
4639
|
-
|
|
4763
|
+
const dir = path12.resolve(outputDir);
|
|
4764
|
+
if (!fs10.existsSync(dir)) {
|
|
4765
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
4640
4766
|
}
|
|
4641
|
-
const indexPath =
|
|
4642
|
-
|
|
4767
|
+
const indexPath = path12.join(dir, "index.html");
|
|
4768
|
+
fs10.writeFileSync(indexPath, html, "utf-8");
|
|
4643
4769
|
return indexPath;
|
|
4644
4770
|
}
|
|
4645
4771
|
|
|
4646
4772
|
// src/commands/report.ts
|
|
4647
|
-
var
|
|
4773
|
+
var RESULTS_DIR2 = ".qat-results";
|
|
4648
4774
|
function registerReportCommand(program2) {
|
|
4649
4775
|
program2.command("report").description("\u751F\u6210\u6D4B\u8BD5\u62A5\u544A - \u805A\u5408\u6240\u6709\u6D4B\u8BD5\u7ED3\u679C\u5E76\u8F93\u51FAHTML").option("-o, --output <dir>", "\u62A5\u544A\u8F93\u51FA\u76EE\u5F55").option("--open", "\u751F\u6210\u540E\u81EA\u52A8\u6253\u5F00\u62A5\u544A", false).action(async (options) => {
|
|
4650
4776
|
try {
|
|
@@ -4679,15 +4805,15 @@ async function executeReport(options) {
|
|
|
4679
4805
|
}
|
|
4680
4806
|
function collectResults() {
|
|
4681
4807
|
const results = [];
|
|
4682
|
-
const resultsPath =
|
|
4683
|
-
if (!
|
|
4808
|
+
const resultsPath = path13.join(process.cwd(), RESULTS_DIR2);
|
|
4809
|
+
if (!fs11.existsSync(resultsPath)) {
|
|
4684
4810
|
return results;
|
|
4685
4811
|
}
|
|
4686
|
-
const files =
|
|
4812
|
+
const files = fs11.readdirSync(resultsPath).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
4687
4813
|
if (files.length > 0) {
|
|
4688
|
-
const latestFile =
|
|
4814
|
+
const latestFile = path13.join(resultsPath, files[0]);
|
|
4689
4815
|
try {
|
|
4690
|
-
const data = JSON.parse(
|
|
4816
|
+
const data = JSON.parse(fs11.readFileSync(latestFile, "utf-8"));
|
|
4691
4817
|
if (Array.isArray(data)) {
|
|
4692
4818
|
results.push(...data);
|
|
4693
4819
|
} else if (data.results) {
|
|
@@ -4699,22 +4825,22 @@ function collectResults() {
|
|
|
4699
4825
|
return results;
|
|
4700
4826
|
}
|
|
4701
4827
|
function saveResultToHistory(reportData) {
|
|
4702
|
-
const resultsPath =
|
|
4703
|
-
if (!
|
|
4704
|
-
|
|
4828
|
+
const resultsPath = path13.join(process.cwd(), RESULTS_DIR2);
|
|
4829
|
+
if (!fs11.existsSync(resultsPath)) {
|
|
4830
|
+
fs11.mkdirSync(resultsPath, { recursive: true });
|
|
4705
4831
|
}
|
|
4706
4832
|
const timestamp = new Date(reportData.timestamp).toISOString().replace(/[:.]/g, "-");
|
|
4707
4833
|
const fileName = `result-${timestamp}.json`;
|
|
4708
|
-
const filePath =
|
|
4709
|
-
|
|
4710
|
-
const files =
|
|
4834
|
+
const filePath = path13.join(resultsPath, fileName);
|
|
4835
|
+
fs11.writeFileSync(filePath, JSON.stringify(reportData, null, 2), "utf-8");
|
|
4836
|
+
const files = fs11.readdirSync(resultsPath).filter((f) => f.startsWith("result-") && f.endsWith(".json")).sort();
|
|
4711
4837
|
while (files.length > 20) {
|
|
4712
4838
|
const oldest = files.shift();
|
|
4713
|
-
|
|
4839
|
+
fs11.unlinkSync(path13.join(resultsPath, oldest));
|
|
4714
4840
|
}
|
|
4715
4841
|
}
|
|
4716
4842
|
function displayReportResult(reportPath, data) {
|
|
4717
|
-
const relativePath =
|
|
4843
|
+
const relativePath = path13.relative(process.cwd(), reportPath);
|
|
4718
4844
|
console.log();
|
|
4719
4845
|
console.log(chalk7.green(" \u2713 \u6D4B\u8BD5\u62A5\u544A\u5DF2\u751F\u6210"));
|
|
4720
4846
|
console.log();
|
|
@@ -4761,19 +4887,19 @@ import chalk8 from "chalk";
|
|
|
4761
4887
|
import ora7 from "ora";
|
|
4762
4888
|
|
|
4763
4889
|
// src/services/visual.ts
|
|
4764
|
-
import
|
|
4765
|
-
import
|
|
4890
|
+
import fs12 from "fs";
|
|
4891
|
+
import path14 from "path";
|
|
4766
4892
|
import pixelmatch from "pixelmatch";
|
|
4767
4893
|
import { PNG } from "pngjs";
|
|
4768
4894
|
function compareImages(baselinePath, currentPath, diffOutputPath, threshold = 0.1) {
|
|
4769
|
-
if (!
|
|
4895
|
+
if (!fs12.existsSync(baselinePath)) {
|
|
4770
4896
|
throw new Error(`\u57FA\u7EBF\u56FE\u7247\u4E0D\u5B58\u5728: ${baselinePath}`);
|
|
4771
4897
|
}
|
|
4772
|
-
if (!
|
|
4898
|
+
if (!fs12.existsSync(currentPath)) {
|
|
4773
4899
|
throw new Error(`\u5F53\u524D\u56FE\u7247\u4E0D\u5B58\u5728: ${currentPath}`);
|
|
4774
4900
|
}
|
|
4775
|
-
const baseline = PNG.sync.read(
|
|
4776
|
-
const current = PNG.sync.read(
|
|
4901
|
+
const baseline = PNG.sync.read(fs12.readFileSync(baselinePath));
|
|
4902
|
+
const current = PNG.sync.read(fs12.readFileSync(currentPath));
|
|
4777
4903
|
if (baseline.width !== current.width || baseline.height !== current.height) {
|
|
4778
4904
|
return {
|
|
4779
4905
|
passed: false,
|
|
@@ -4801,11 +4927,11 @@ function compareImages(baselinePath, currentPath, diffOutputPath, threshold = 0.
|
|
|
4801
4927
|
const passed = diffRatio <= threshold;
|
|
4802
4928
|
let diffPath;
|
|
4803
4929
|
if (diffPixels > 0) {
|
|
4804
|
-
const diffDir =
|
|
4805
|
-
if (!
|
|
4806
|
-
|
|
4930
|
+
const diffDir = path14.dirname(diffOutputPath);
|
|
4931
|
+
if (!fs12.existsSync(diffDir)) {
|
|
4932
|
+
fs12.mkdirSync(diffDir, { recursive: true });
|
|
4807
4933
|
}
|
|
4808
|
-
|
|
4934
|
+
fs12.writeFileSync(diffOutputPath, PNG.sync.write(diff));
|
|
4809
4935
|
diffPath = diffOutputPath;
|
|
4810
4936
|
}
|
|
4811
4937
|
return {
|
|
@@ -4819,69 +4945,69 @@ function compareImages(baselinePath, currentPath, diffOutputPath, threshold = 0.
|
|
|
4819
4945
|
};
|
|
4820
4946
|
}
|
|
4821
4947
|
function createBaseline(currentPath, baselinePath) {
|
|
4822
|
-
if (!
|
|
4948
|
+
if (!fs12.existsSync(currentPath)) {
|
|
4823
4949
|
throw new Error(`\u5F53\u524D\u622A\u56FE\u4E0D\u5B58\u5728: ${currentPath}`);
|
|
4824
4950
|
}
|
|
4825
|
-
const baselineDir =
|
|
4826
|
-
if (!
|
|
4827
|
-
|
|
4951
|
+
const baselineDir = path14.dirname(baselinePath);
|
|
4952
|
+
if (!fs12.existsSync(baselineDir)) {
|
|
4953
|
+
fs12.mkdirSync(baselineDir, { recursive: true });
|
|
4828
4954
|
}
|
|
4829
|
-
|
|
4955
|
+
fs12.copyFileSync(currentPath, baselinePath);
|
|
4830
4956
|
return baselinePath;
|
|
4831
4957
|
}
|
|
4832
4958
|
function updateAllBaselines(currentDir, baselineDir) {
|
|
4833
4959
|
const updated = [];
|
|
4834
|
-
if (!
|
|
4960
|
+
if (!fs12.existsSync(currentDir)) {
|
|
4835
4961
|
return updated;
|
|
4836
4962
|
}
|
|
4837
|
-
const files =
|
|
4963
|
+
const files = fs12.readdirSync(currentDir).filter((f) => f.endsWith(".png"));
|
|
4838
4964
|
for (const file of files) {
|
|
4839
|
-
const currentPath =
|
|
4840
|
-
const baselinePath =
|
|
4841
|
-
const baselineDirAbs =
|
|
4842
|
-
if (!
|
|
4843
|
-
|
|
4965
|
+
const currentPath = path14.join(currentDir, file);
|
|
4966
|
+
const baselinePath = path14.join(baselineDir, file);
|
|
4967
|
+
const baselineDirAbs = path14.dirname(baselinePath);
|
|
4968
|
+
if (!fs12.existsSync(baselineDirAbs)) {
|
|
4969
|
+
fs12.mkdirSync(baselineDirAbs, { recursive: true });
|
|
4844
4970
|
}
|
|
4845
|
-
|
|
4971
|
+
fs12.copyFileSync(currentPath, baselinePath);
|
|
4846
4972
|
updated.push(file);
|
|
4847
4973
|
}
|
|
4848
4974
|
return updated;
|
|
4849
4975
|
}
|
|
4850
4976
|
function cleanBaselines(baselineDir) {
|
|
4851
|
-
if (!
|
|
4977
|
+
if (!fs12.existsSync(baselineDir)) {
|
|
4852
4978
|
return 0;
|
|
4853
4979
|
}
|
|
4854
|
-
const files =
|
|
4980
|
+
const files = fs12.readdirSync(baselineDir).filter((f) => f.endsWith(".png"));
|
|
4855
4981
|
let count = 0;
|
|
4856
4982
|
for (const file of files) {
|
|
4857
|
-
|
|
4983
|
+
fs12.unlinkSync(path14.join(baselineDir, file));
|
|
4858
4984
|
count++;
|
|
4859
4985
|
}
|
|
4860
4986
|
return count;
|
|
4861
4987
|
}
|
|
4862
4988
|
function cleanDiffs(diffDir) {
|
|
4863
|
-
if (!
|
|
4989
|
+
if (!fs12.existsSync(diffDir)) {
|
|
4864
4990
|
return 0;
|
|
4865
4991
|
}
|
|
4866
|
-
const files =
|
|
4992
|
+
const files = fs12.readdirSync(diffDir).filter((f) => f.endsWith(".png"));
|
|
4867
4993
|
let count = 0;
|
|
4868
4994
|
for (const file of files) {
|
|
4869
|
-
|
|
4995
|
+
fs12.unlinkSync(path14.join(diffDir, file));
|
|
4870
4996
|
count++;
|
|
4871
4997
|
}
|
|
4872
4998
|
return count;
|
|
4873
4999
|
}
|
|
4874
5000
|
function compareDirectories(baselineDir, currentDir, diffDir, threshold = 0.1) {
|
|
4875
5001
|
const results = [];
|
|
4876
|
-
if (!
|
|
5002
|
+
if (!fs12.existsSync(currentDir)) {
|
|
4877
5003
|
return results;
|
|
4878
5004
|
}
|
|
4879
|
-
const currentFiles =
|
|
5005
|
+
const currentFiles = fs12.readdirSync(currentDir).filter((f) => f.endsWith(".png"));
|
|
4880
5006
|
for (const file of currentFiles) {
|
|
4881
|
-
const currentPath =
|
|
4882
|
-
const baselinePath =
|
|
4883
|
-
const diffPath =
|
|
4884
|
-
if (!
|
|
5007
|
+
const currentPath = path14.join(currentDir, file);
|
|
5008
|
+
const baselinePath = path14.join(baselineDir, file);
|
|
5009
|
+
const diffPath = path14.join(diffDir, file);
|
|
5010
|
+
if (!fs12.existsSync(baselinePath)) {
|
|
4885
5011
|
createBaseline(currentPath, baselinePath);
|
|
4886
5012
|
results.push({
|
|
4887
5013
|
passed: true,
|
|
@@ -4913,8 +5039,8 @@ function compareDirectories(baselineDir, currentDir, diffDir, threshold = 0.1) {
|
|
|
4913
5039
|
}
|
|
4914
5040
|
|
|
4915
5041
|
// src/commands/visual.ts
|
|
4916
|
-
import
|
|
4917
|
-
import
|
|
5042
|
+
import fs13 from "fs";
|
|
5043
|
+
import path15 from "path";
|
|
4918
5044
|
function registerVisualCommand(program2) {
|
|
4919
5045
|
program2.command("visual").description("\u89C6\u89C9\u56DE\u5F52\u6D4B\u8BD5 - \u622A\u56FE\u6BD4\u5BF9\u4E0E\u57FA\u7EBF\u7BA1\u7406").argument("<action>", "\u64CD\u4F5C\u7C7B\u578B (test|approve|clean)").option("--threshold <number>", "\u50CF\u7D20\u5DEE\u5F02\u9608\u503C (0-1)", "0.1").action(async (action, options) => {
|
|
4920
5046
|
try {
|
|
@@ -4989,12 +5115,12 @@ async function runVisualTest(threshold, baselineDir, diffDir, config) {
|
|
|
4989
5115
|
}
|
|
4990
5116
|
function findCurrentScreenshotsDir(baselineDir) {
|
|
4991
5117
|
const possibleDirs = [
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
5118
|
+
path15.join(process.cwd(), "test-results"),
|
|
5119
|
+
path15.join(process.cwd(), "tests", "visual", "current"),
|
|
5120
|
+
path15.join(process.cwd(), baselineDir, "..", "current")
|
|
4995
5121
|
];
|
|
4996
5122
|
for (const dir of possibleDirs) {
|
|
4997
|
-
if (
|
|
5123
|
+
if (fs13.existsSync(dir)) {
|
|
4998
5124
|
const pngs = findPngFiles(dir);
|
|
4999
5125
|
if (pngs.length > 0) return dir;
|
|
5000
5126
|
}
|
|
@@ -5004,10 +5130,10 @@ function findCurrentScreenshotsDir(baselineDir) {
|
|
|
5004
5130
|
function findPngFiles(dir) {
|
|
5005
5131
|
const files = [];
|
|
5006
5132
|
function walk(d) {
|
|
5007
|
-
if (!
|
|
5008
|
-
const entries =
|
|
5133
|
+
if (!fs13.existsSync(d)) return;
|
|
5134
|
+
const entries = fs13.readdirSync(d, { withFileTypes: true });
|
|
5009
5135
|
for (const entry of entries) {
|
|
5010
|
-
const fullPath =
|
|
5136
|
+
const fullPath = path15.join(d, entry.name);
|
|
5011
5137
|
if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
5012
5138
|
walk(fullPath);
|
|
5013
5139
|
} else if (entry.name.endsWith(".png")) {
|
|
@@ -5036,7 +5162,7 @@ function displayVisualResults(results, threshold) {
|
|
|
5036
5162
|
console.log();
|
|
5037
5163
|
console.log(chalk8.green(" \u901A\u8FC7\u7684\u622A\u56FE:"));
|
|
5038
5164
|
for (const result of passed) {
|
|
5039
|
-
const name =
|
|
5165
|
+
const name = path15.basename(result.baselinePath);
|
|
5040
5166
|
if (result.totalPixels > 0) {
|
|
5041
5167
|
const diffPct = (result.diffRatio * 100).toFixed(2);
|
|
5042
5168
|
console.log(chalk8.green(` \u2713 ${name} (\u5DEE\u5F02: ${diffPct}%)`));
|
|
@@ -5049,7 +5175,7 @@ function displayVisualResults(results, threshold) {
|
|
|
5049
5175
|
console.log();
|
|
5050
5176
|
console.log(chalk8.red(" \u5931\u8D25\u7684\u622A\u56FE:"));
|
|
5051
5177
|
for (const result of failed) {
|
|
5052
|
-
const name =
|
|
5178
|
+
const name = path15.basename(result.baselinePath);
|
|
5053
5179
|
if (result.diffPixels === -1) {
|
|
5054
5180
|
console.log(chalk8.red(` \u2717 ${name} (\u5C3A\u5BF8\u4E0D\u5339\u914D)`));
|
|
5055
5181
|
} else {
|
|
@@ -5057,7 +5183,7 @@ function displayVisualResults(results, threshold) {
|
|
|
5057
5183
|
console.log(chalk8.red(` \u2717 ${name} (\u5DEE\u5F02: ${diffPct}%)`));
|
|
5058
5184
|
}
|
|
5059
5185
|
if (result.diffPath) {
|
|
5060
|
-
console.log(chalk8.gray(` \u5DEE\u5F02\u56FE: ${
|
|
5186
|
+
console.log(chalk8.gray(` \u5DEE\u5F02\u56FE: ${path15.relative(process.cwd(), result.diffPath)}`));
|
|
5061
5187
|
}
|
|
5062
5188
|
}
|
|
5063
5189
|
console.log();
|
|
@@ -5103,8 +5229,8 @@ import chalk9 from "chalk";
|
|
|
5103
5229
|
import inquirer4 from "inquirer";
|
|
5104
5230
|
import ora8 from "ora";
|
|
5105
5231
|
import { execFile as execFile4 } from "child_process";
|
|
5106
|
-
import
|
|
5107
|
-
import
|
|
5232
|
+
import fs14 from "fs";
|
|
5233
|
+
import path16 from "path";
|
|
5108
5234
|
var DEPENDENCY_GROUPS = [
|
|
5109
5235
|
{
|
|
5110
5236
|
name: "Vitest (\u5355\u5143/\u7EC4\u4EF6/API\u6D4B\u8BD5)",
|
|
@@ -5138,7 +5264,7 @@ function registerSetupCommand(program2) {
|
|
|
5138
5264
|
async function executeSetup(options) {
|
|
5139
5265
|
console.log(chalk9.cyan("\n QAT \u4F9D\u8D56\u5B89\u88C5\u5668\n"));
|
|
5140
5266
|
const projectInfo = detectProject();
|
|
5141
|
-
if (!
|
|
5267
|
+
if (!fs14.existsSync(path16.join(process.cwd(), "package.json"))) {
|
|
5142
5268
|
throw new Error("\u672A\u627E\u5230 package.json\uFF0C\u8BF7\u5728\u9879\u76EE\u6839\u76EE\u5F55\u6267\u884C\u6B64\u547D\u4EE4");
|
|
5143
5269
|
}
|
|
5144
5270
|
if (projectInfo.frameworkConfidence > 0) {
|
|
@@ -5172,8 +5298,8 @@ async function executeSetup(options) {
|
|
|
5172
5298
|
}
|
|
5173
5299
|
]);
|
|
5174
5300
|
if (chooseDir !== "root") {
|
|
5175
|
-
installDir =
|
|
5176
|
-
if (!
|
|
5301
|
+
installDir = path16.join(process.cwd(), chooseDir);
|
|
5302
|
+
if (!fs14.existsSync(path16.join(installDir, "package.json"))) {
|
|
5177
5303
|
throw new Error(`${chooseDir} \u4E0B\u6CA1\u6709 package.json`);
|
|
5178
5304
|
}
|
|
5179
5305
|
}
|
|
@@ -5212,7 +5338,7 @@ ${allPackages.map((p) => ` - ${p}`).join("\n")}`,
|
|
|
5212
5338
|
const pm = getPackageManager(projectInfo.packageManager);
|
|
5213
5339
|
const installCmd = pm === "npm" ? "npm install -D" : pm === "yarn" ? "yarn add -D" : pm === "pnpm" ? "pnpm add -D" : "bun add -D";
|
|
5214
5340
|
for (const group of selectedGroups) {
|
|
5215
|
-
const dirHint = installDir !== process.cwd() ? ` (\u5728 ${
|
|
5341
|
+
const dirHint = installDir !== process.cwd() ? ` (\u5728 ${path16.relative(process.cwd(), installDir) || installDir})` : "";
|
|
5216
5342
|
console.log(chalk9.white(` ${installCmd} ${group.packages.join(" ")}${dirHint}`));
|
|
5217
5343
|
if (group.postInstall) {
|
|
5218
5344
|
for (const cmd of group.postInstall) {
|
|
@@ -5493,7 +5619,7 @@ async function executeChange(_options) {
|
|
|
5493
5619
|
}
|
|
5494
5620
|
|
|
5495
5621
|
// src/cli.ts
|
|
5496
|
-
var VERSION = "0.2.
|
|
5622
|
+
var VERSION = "0.2.8";
|
|
5497
5623
|
function printLogo() {
|
|
5498
5624
|
const logo = `
|
|
5499
5625
|
${chalk12.bold.cyan(" ___ _ _ _ _ _____ _ _ ")}
|