mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 08:04:56 +03:00
feat(web-test): test CLI принимает несколько путей + флаг --url, fail-fast валидация
Привод контракта `run.mjs test` к общей практике (pytest/jest/playwright): - позиционные аргументы = пути к тестам, можно несколько (`test a.test.mjs b.test.mjs dir/`); discoverTests объединяет, дедуплицирует, сортирует (порядок по числовым префиксам сохраняется); - URL переехал из первого позиционного во флаг --url= (по умолчанию из webtest.config.mjs) — раньше url-первым путал: второй путь уходил в page.goto() и падал с криптовым 'Cannot navigate to invalid URL'; - fail-fast: несуществующий путь → понятная ошибка вместо падения goto; аргумент, похожий на URL, подсказывает про --url=. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// web-test cli/commands/test v1.2 — regression test runner
|
||||
// web-test cli/commands/test v1.3 — regression test runner
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import { existsSync, writeFileSync, mkdirSync } from 'fs';
|
||||
import { resolve, dirname, basename, relative } from 'path';
|
||||
@@ -20,11 +20,12 @@ export async function cmdTest(rawArgs) {
|
||||
|
||||
// Parse flags
|
||||
const opts = { bail: false, retry: 0, timeout: 30000, report: null, format: 'json', screenshot: null, reportDir: null, record: false };
|
||||
let tags = null, grep = null;
|
||||
let tags = null, grep = null, urlFlag = null;
|
||||
const positional = [];
|
||||
for (const a of ownArgs) {
|
||||
if (a.startsWith('--tags=')) tags = a.slice(7).split(',');
|
||||
else if (a.startsWith('--grep=')) grep = new RegExp(a.slice(7), 'i');
|
||||
else if (a.startsWith('--url=')) urlFlag = a.slice(6);
|
||||
else if (a === '--bail') opts.bail = true;
|
||||
else if (a.startsWith('--retry=')) opts.retry = parseInt(a.slice(8)) || 0;
|
||||
else if (a.startsWith('--timeout=')) opts.timeout = parseInt(a.slice(10)) || 30000;
|
||||
@@ -36,20 +37,28 @@ export async function cmdTest(rawArgs) {
|
||||
else if (!a.startsWith('--')) positional.push(a);
|
||||
}
|
||||
|
||||
// Determine URL and test path
|
||||
let url, testPath;
|
||||
if (positional.length === 2) {
|
||||
url = positional[0];
|
||||
testPath = resolve(positional[1]);
|
||||
} else if (positional.length === 1) {
|
||||
testPath = resolve(positional[0]);
|
||||
} else {
|
||||
die('Usage: node run.mjs test [url] <dir|file> [--tags=...] [--bail] [--retry=N] [--timeout=ms] [--report=path]');
|
||||
// Positional args are ALWAYS test paths (one or many). URL comes from --url= or config
|
||||
// (see webtest.config.mjs). This matches pytest/jest/playwright; a positional that looks
|
||||
// like a URL is a mistake → fail fast with a hint instead of feeding it to page.goto().
|
||||
const isUrl = (s) => /^https?:\/\//i.test(s);
|
||||
let url = urlFlag || null;
|
||||
const testPaths = [...positional];
|
||||
if (testPaths.length === 0) {
|
||||
die('Usage: node run.mjs test <dir|file>... [--url=URL] [--tags=...] [--grep=...] [--bail] [--retry=N] [--timeout=ms] [--report=path]');
|
||||
}
|
||||
for (const p of testPaths) {
|
||||
if (existsSync(resolve(p))) continue;
|
||||
if (isUrl(p)) {
|
||||
die(`"${p}" looks like a URL — use --url=<url>; positional args are test paths.`);
|
||||
}
|
||||
die(`Test path not found: "${p}". To run a subset use --grep= / --tags=, or pass an existing dir/file.`);
|
||||
}
|
||||
|
||||
// Load config if exists
|
||||
const isFile = testPath.endsWith('.test.mjs');
|
||||
const testDir = isFile ? dirname(testPath) : testPath;
|
||||
// Load config if exists. config (webtest.config.mjs) and hooks (_hooks.mjs) resolve from
|
||||
// the FIRST path's directory — list paths from the same suite folder.
|
||||
const firstPath = resolve(testPaths[0]);
|
||||
const isFile = firstPath.endsWith('.test.mjs');
|
||||
const testDir = isFile ? dirname(firstPath) : firstPath;
|
||||
const configPath = resolve(testDir, 'webtest.config.mjs');
|
||||
let config = {};
|
||||
if (existsSync(configPath)) {
|
||||
@@ -110,8 +119,8 @@ export async function cmdTest(rawArgs) {
|
||||
}
|
||||
|
||||
// Discover test files
|
||||
const testFiles = discoverTests(testPath);
|
||||
if (!testFiles.length) die(`No *.test.mjs files found in ${testPath}`);
|
||||
const testFiles = discoverTests(testPaths);
|
||||
if (!testFiles.length) die(`No *.test.mjs files found in ${testPaths.join(', ')}`);
|
||||
|
||||
// Import and filter tests
|
||||
const tests = [];
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
// web-test cli/test-runner/discover v1.0 — test file discovery + state reset between tests
|
||||
// web-test cli/test-runner/discover v1.1 — test file discovery + state reset between tests
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import { existsSync, readdirSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
|
||||
export function discoverTests(testPath) {
|
||||
if (testPath.endsWith('.test.mjs')) return existsSync(testPath) ? [testPath] : [];
|
||||
// Accepts a single path or an array of paths (files and/or dirs). Each .test.mjs file is
|
||||
// taken directly; each directory is walked recursively (skipping _ / . prefixes). Results
|
||||
// are deduped and sorted — sorting preserves the numeric-prefix order the suite relies on
|
||||
// (00-, 01-, …) even when paths are listed out of order.
|
||||
export function discoverTests(testPaths) {
|
||||
const paths = Array.isArray(testPaths) ? testPaths : [testPaths];
|
||||
const files = [];
|
||||
function walk(dir) {
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
@@ -14,8 +18,15 @@ export function discoverTests(testPath) {
|
||||
else if (entry.name.endsWith('.test.mjs')) files.push(full);
|
||||
}
|
||||
}
|
||||
walk(testPath);
|
||||
return files.sort();
|
||||
for (const p of paths) {
|
||||
const full = resolve(p);
|
||||
if (full.endsWith('.test.mjs')) {
|
||||
if (existsSync(full)) files.push(full);
|
||||
} else if (existsSync(full)) {
|
||||
walk(full);
|
||||
}
|
||||
}
|
||||
return [...new Set(files)].sort();
|
||||
}
|
||||
|
||||
export async function resetState(ctx) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// web-test cli/util v1.1 — generic helpers for CLI commands
|
||||
// web-test cli/util v1.2 — generic helpers for CLI commands
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
export function out(obj) {
|
||||
@@ -85,7 +85,7 @@ Commands:
|
||||
shot [file] Take screenshot (default: shot.png)
|
||||
stop Logout and close browser
|
||||
status Check session status
|
||||
test [url] <dir|file> Run regression tests (*.test.mjs)
|
||||
test <dir|file>... Run regression tests (*.test.mjs); accepts multiple paths
|
||||
|
||||
Options for exec:
|
||||
--no-record Skip video recording (record() becomes no-op)
|
||||
@@ -95,6 +95,7 @@ Global options (any command):
|
||||
Default: on (env: WEB_TEST_PRESERVE_CLIPBOARD=0 to disable globally).
|
||||
|
||||
Options for test:
|
||||
--url=URL Override the base URL (default: from webtest.config.mjs)
|
||||
--tags=smoke,crud Filter tests by tags
|
||||
--grep=pattern Filter tests by name (regex)
|
||||
--bail Stop on first failure
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// web-test run v1.17 — CLI entry-point (распилено по cli/)
|
||||
// web-test run v1.18 — CLI entry-point (распилено по cli/)
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
/**
|
||||
* CLI runner for 1C web client automation.
|
||||
@@ -14,7 +14,7 @@
|
||||
* node src/run.mjs shot [file] — take screenshot
|
||||
* node src/run.mjs stop — logout + close browser
|
||||
* node src/run.mjs status — check session
|
||||
* node src/run.mjs test [url] <dir|file> — run regression tests
|
||||
* node src/run.mjs test <dir|file>... [--url] — run regression tests
|
||||
*
|
||||
* Внутренности живут в cli/: util, session, exec-context, server,
|
||||
* commands/{start,run,exec,shot,stop,status,test}, test-runner/*.
|
||||
|
||||
Reference in New Issue
Block a user