@application-kit/acceptance 0.0.0-stage → 0.1.1
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/CHANGELOG.md +13 -0
- package/dist/cjs/bin/application-scenarios.d.ts +2 -0
- package/dist/cjs/bin/application-scenarios.js +15 -0
- package/dist/cjs/bin/index.d.ts +1 -0
- package/dist/cjs/bin/index.js +17 -0
- package/dist/cjs/cadence/index.d.ts +1 -0
- package/dist/cjs/cadence/index.js +17 -0
- package/dist/cjs/cadence/resolve-cadence.d.ts +8 -0
- package/dist/cjs/cadence/resolve-cadence.js +18 -0
- package/dist/cjs/environments/environment.interface.d.ts +5 -0
- package/dist/cjs/environments/environment.interface.js +2 -0
- package/dist/cjs/environments/index.d.ts +3 -0
- package/dist/cjs/environments/index.js +19 -0
- package/dist/cjs/environments/resolve-environment.d.ts +10 -0
- package/dist/cjs/environments/resolve-environment.js +20 -0
- package/dist/cjs/environments/secrets.d.ts +4 -0
- package/dist/cjs/environments/secrets.js +14 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +18 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/scenarios/acceptance-class.d.ts +26 -0
- package/dist/cjs/scenarios/acceptance-class.js +100 -0
- package/dist/cjs/scenarios/board.d.ts +6 -0
- package/dist/cjs/scenarios/board.js +98 -0
- package/dist/cjs/scenarios/classes.d.ts +16 -0
- package/dist/cjs/scenarios/classes.js +48 -0
- package/dist/cjs/scenarios/classify.d.ts +35 -0
- package/dist/cjs/scenarios/classify.js +110 -0
- package/dist/cjs/scenarios/config.d.ts +17 -0
- package/dist/cjs/scenarios/config.js +67 -0
- package/dist/cjs/scenarios/coverage-data.d.ts +53 -0
- package/dist/cjs/scenarios/coverage-data.js +118 -0
- package/dist/cjs/scenarios/coverage-json.d.ts +2 -0
- package/dist/cjs/scenarios/coverage-json.js +94 -0
- package/dist/cjs/scenarios/coverage-open.d.ts +16 -0
- package/dist/cjs/scenarios/coverage-open.js +76 -0
- package/dist/cjs/scenarios/coverage-page.d.ts +2 -0
- package/dist/cjs/scenarios/coverage-page.js +108 -0
- package/dist/cjs/scenarios/coverage-removed.d.ts +13 -0
- package/dist/cjs/scenarios/coverage-removed.js +30 -0
- package/dist/cjs/scenarios/coverage.d.ts +26 -0
- package/dist/cjs/scenarios/coverage.js +91 -0
- package/dist/cjs/scenarios/dates.d.ts +8 -0
- package/dist/cjs/scenarios/dates.js +93 -0
- package/dist/cjs/scenarios/declared.d.ts +18 -0
- package/dist/cjs/scenarios/declared.js +69 -0
- package/dist/cjs/scenarios/features.d.ts +13 -0
- package/dist/cjs/scenarios/features.js +112 -0
- package/dist/cjs/scenarios/gherkin.d.ts +10 -0
- package/dist/cjs/scenarios/gherkin.js +75 -0
- package/dist/cjs/scenarios/git.d.ts +15 -0
- package/dist/cjs/scenarios/git.js +83 -0
- package/dist/cjs/scenarios/index.d.ts +27 -0
- package/dist/cjs/scenarios/index.js +43 -0
- package/dist/cjs/scenarios/inventory.d.ts +8 -0
- package/dist/cjs/scenarios/inventory.js +55 -0
- package/dist/cjs/scenarios/links.d.ts +4 -0
- package/dist/cjs/scenarios/links.js +99 -0
- package/dist/cjs/scenarios/lint-inputs.d.ts +12 -0
- package/dist/cjs/scenarios/lint-inputs.js +118 -0
- package/dist/cjs/scenarios/lint.d.ts +8 -0
- package/dist/cjs/scenarios/lint.js +94 -0
- package/dist/cjs/scenarios/projects.d.ts +21 -0
- package/dist/cjs/scenarios/projects.js +115 -0
- package/dist/cjs/scenarios/python-text.d.ts +11 -0
- package/dist/cjs/scenarios/python-text.js +95 -0
- package/dist/cjs/scenarios/remote.d.ts +6 -0
- package/dist/cjs/scenarios/remote.js +29 -0
- package/dist/cjs/scenarios/removed-list.d.ts +15 -0
- package/dist/cjs/scenarios/removed-list.js +62 -0
- package/dist/cjs/scenarios/report.d.ts +6 -0
- package/dist/cjs/scenarios/report.js +10 -0
- package/dist/cjs/scenarios/run.d.ts +10 -0
- package/dist/cjs/scenarios/run.js +145 -0
- package/dist/cjs/scenarios/task-links.d.ts +15 -0
- package/dist/cjs/scenarios/task-links.js +54 -0
- package/dist/esm/bin/application-scenarios.d.ts +2 -0
- package/dist/esm/bin/application-scenarios.js +10 -0
- package/dist/esm/bin/index.d.ts +1 -0
- package/dist/esm/bin/index.js +1 -0
- package/dist/esm/cadence/index.d.ts +1 -0
- package/dist/esm/cadence/index.js +1 -0
- package/dist/esm/cadence/resolve-cadence.d.ts +8 -0
- package/dist/esm/cadence/resolve-cadence.js +14 -0
- package/dist/esm/environments/environment.interface.d.ts +5 -0
- package/dist/esm/environments/environment.interface.js +1 -0
- package/dist/esm/environments/index.d.ts +3 -0
- package/dist/esm/environments/index.js +3 -0
- package/dist/esm/environments/resolve-environment.d.ts +10 -0
- package/dist/esm/environments/resolve-environment.js +16 -0
- package/dist/esm/environments/secrets.d.ts +4 -0
- package/dist/esm/environments/secrets.js +9 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/scenarios/acceptance-class.d.ts +26 -0
- package/dist/esm/scenarios/acceptance-class.js +93 -0
- package/dist/esm/scenarios/board.d.ts +6 -0
- package/dist/esm/scenarios/board.js +92 -0
- package/dist/esm/scenarios/classes.d.ts +16 -0
- package/dist/esm/scenarios/classes.js +42 -0
- package/dist/esm/scenarios/classify.d.ts +35 -0
- package/dist/esm/scenarios/classify.js +106 -0
- package/dist/esm/scenarios/config.d.ts +17 -0
- package/dist/esm/scenarios/config.js +58 -0
- package/dist/esm/scenarios/coverage-data.d.ts +53 -0
- package/dist/esm/scenarios/coverage-data.js +109 -0
- package/dist/esm/scenarios/coverage-json.d.ts +2 -0
- package/dist/esm/scenarios/coverage-json.js +90 -0
- package/dist/esm/scenarios/coverage-open.d.ts +16 -0
- package/dist/esm/scenarios/coverage-open.js +71 -0
- package/dist/esm/scenarios/coverage-page.d.ts +2 -0
- package/dist/esm/scenarios/coverage-page.js +104 -0
- package/dist/esm/scenarios/coverage-removed.d.ts +13 -0
- package/dist/esm/scenarios/coverage-removed.js +26 -0
- package/dist/esm/scenarios/coverage.d.ts +26 -0
- package/dist/esm/scenarios/coverage.js +84 -0
- package/dist/esm/scenarios/dates.d.ts +8 -0
- package/dist/esm/scenarios/dates.js +87 -0
- package/dist/esm/scenarios/declared.d.ts +18 -0
- package/dist/esm/scenarios/declared.js +62 -0
- package/dist/esm/scenarios/features.d.ts +13 -0
- package/dist/esm/scenarios/features.js +96 -0
- package/dist/esm/scenarios/gherkin.d.ts +10 -0
- package/dist/esm/scenarios/gherkin.js +68 -0
- package/dist/esm/scenarios/git.d.ts +15 -0
- package/dist/esm/scenarios/git.js +72 -0
- package/dist/esm/scenarios/index.d.ts +27 -0
- package/dist/esm/scenarios/index.js +27 -0
- package/dist/esm/scenarios/inventory.d.ts +8 -0
- package/dist/esm/scenarios/inventory.js +48 -0
- package/dist/esm/scenarios/links.d.ts +4 -0
- package/dist/esm/scenarios/links.js +95 -0
- package/dist/esm/scenarios/lint-inputs.d.ts +12 -0
- package/dist/esm/scenarios/lint-inputs.js +110 -0
- package/dist/esm/scenarios/lint.d.ts +8 -0
- package/dist/esm/scenarios/lint.js +89 -0
- package/dist/esm/scenarios/projects.d.ts +21 -0
- package/dist/esm/scenarios/projects.js +104 -0
- package/dist/esm/scenarios/python-text.d.ts +11 -0
- package/dist/esm/scenarios/python-text.js +81 -0
- package/dist/esm/scenarios/remote.d.ts +6 -0
- package/dist/esm/scenarios/remote.js +24 -0
- package/dist/esm/scenarios/removed-list.d.ts +15 -0
- package/dist/esm/scenarios/removed-list.js +57 -0
- package/dist/esm/scenarios/report.d.ts +6 -0
- package/dist/esm/scenarios/report.js +6 -0
- package/dist/esm/scenarios/run.d.ts +10 -0
- package/dist/esm/scenarios/run.js +141 -0
- package/dist/esm/scenarios/task-links.d.ts +15 -0
- package/dist/esm/scenarios/task-links.js +50 -0
- package/package.json +65 -3
- package/README.md +0 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @application-kit/acceptance
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8458cb7: `classes` и `links` видят story продукта, когда репозиторий тестов — его подкаталог: задачи читаются с коммита текущего checkout. Сценарии критериев старого формата `AC-22.10.` засчитываются своей story.
|
|
8
|
+
|
|
9
|
+
## 0.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 572f086: Новый пакет: команда `application-scenarios` для репозитория приёмочных тестов (`lint`, `removed`, `links`, `classes`, `coverage`, `inventory`), каденс прогона, реестр окружений и чтение учётных данных окружения. Вывод `inventory` несёт версию контракта `contract`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const node_process_1 = __importDefault(require("node:process"));
|
|
8
|
+
const run_js_1 = require("../scenarios/run.js");
|
|
9
|
+
const main = async () => {
|
|
10
|
+
const result = await (0, run_js_1.runScenarios)(node_process_1.default.argv.slice(2), { cwd: node_process_1.default.cwd(), now: new Date() });
|
|
11
|
+
node_process_1.default.stdout.write(result.stdout);
|
|
12
|
+
node_process_1.default.stderr.write(result.stderr);
|
|
13
|
+
node_process_1.default.exitCode = result.code;
|
|
14
|
+
};
|
|
15
|
+
void main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './application-scenarios.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./application-scenarios.js"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './resolve-cadence.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./resolve-cadence.js"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface CadenceInterface {
|
|
2
|
+
readonly exclude: readonly string[];
|
|
3
|
+
readonly include: readonly string[];
|
|
4
|
+
readonly name: string;
|
|
5
|
+
}
|
|
6
|
+
type Variables = Readonly<Record<string, string | undefined>>;
|
|
7
|
+
export declare const resolveCadence: (variables: Variables) => CadenceInterface;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveCadence = void 0;
|
|
4
|
+
const regression = { exclude: ['@nightly'], include: [], name: 'regression' };
|
|
5
|
+
const smoke = { exclude: [], include: ['@smoke'], name: 'smoke' };
|
|
6
|
+
const nightly = { exclude: [], include: ['@nightly'], name: 'nightly' };
|
|
7
|
+
const cadences = [nightly, regression, smoke];
|
|
8
|
+
const resolveCadence = (variables) => {
|
|
9
|
+
const requested = variables.ACCEPTANCE_CADENCE ?? '';
|
|
10
|
+
const name = requested === '' ? regression.name : requested;
|
|
11
|
+
const cadence = cadences.find((candidate) => candidate.name === name);
|
|
12
|
+
if (cadence === undefined) {
|
|
13
|
+
const known = cadences.map((candidate) => candidate.name).join(', ');
|
|
14
|
+
throw new Error(`ACCEPTANCE_CADENCE: каденса «${name}» нет (есть: ${known})`);
|
|
15
|
+
}
|
|
16
|
+
return cadence;
|
|
17
|
+
};
|
|
18
|
+
exports.resolveCadence = resolveCadence;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./environment.interface.js"), exports);
|
|
18
|
+
__exportStar(require("./resolve-environment.js"), exports);
|
|
19
|
+
__exportStar(require("./secrets.js"), exports);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EnvironmentInterface } from './environment.interface.js';
|
|
2
|
+
export interface EnvironmentRegistryInterface {
|
|
3
|
+
readonly environments: readonly EnvironmentInterface[];
|
|
4
|
+
readonly fallback: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ResolvedEnvironmentInterface extends EnvironmentInterface {
|
|
7
|
+
readonly baseUrl: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
export type VariablesType = Readonly<Record<string, string | undefined>>;
|
|
10
|
+
export declare const resolveEnvironment: (registry: EnvironmentRegistryInterface, variables: VariablesType) => ResolvedEnvironmentInterface;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveEnvironment = void 0;
|
|
4
|
+
const findEnvironment = (registry, name) => {
|
|
5
|
+
const environment = registry.environments.find((candidate) => candidate.name === name);
|
|
6
|
+
if (environment === undefined) {
|
|
7
|
+
const known = registry.environments.map((candidate) => candidate.name).join(', ');
|
|
8
|
+
throw new Error(`ACCEPTANCE_ENV: окружения «${name}» нет в реестре (есть: ${known})`);
|
|
9
|
+
}
|
|
10
|
+
return environment;
|
|
11
|
+
};
|
|
12
|
+
const baseUrlOf = (variables) => {
|
|
13
|
+
const baseUrl = variables.ACCEPTANCE_BASE_URL ?? '';
|
|
14
|
+
return baseUrl === '' ? undefined : new URL(baseUrl).href;
|
|
15
|
+
};
|
|
16
|
+
const resolveEnvironment = (registry, variables) => {
|
|
17
|
+
const environment = findEnvironment(registry, variables.ACCEPTANCE_ENV ?? registry.fallback);
|
|
18
|
+
return { ...environment, baseUrl: baseUrlOf(variables) };
|
|
19
|
+
};
|
|
20
|
+
exports.resolveEnvironment = resolveEnvironment;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EnvironmentInterface } from './environment.interface.js';
|
|
2
|
+
import type { VariablesType } from './resolve-environment.js';
|
|
3
|
+
export declare const secretVariable: (environment: EnvironmentInterface, name: string) => string;
|
|
4
|
+
export declare const readSecret: (environment: EnvironmentInterface, name: string, variables: VariablesType) => string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readSecret = exports.secretVariable = void 0;
|
|
4
|
+
const secretVariable = (environment, name) => `${environment.secretsPrefix}${name}`;
|
|
5
|
+
exports.secretVariable = secretVariable;
|
|
6
|
+
const readSecret = (environment, name, variables) => {
|
|
7
|
+
const variable = (0, exports.secretVariable)(environment, name);
|
|
8
|
+
const value = variables[variable] ?? '';
|
|
9
|
+
if (value === '') {
|
|
10
|
+
throw new Error(`${variable}: переменная с учётными данными не задана`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
exports.readSecret = readSecret;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./cadence/index.js"), exports);
|
|
18
|
+
__exportStar(require("./environments/index.js"), exports);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ConfigInterface } from './config.js';
|
|
2
|
+
import type { ScenarioInterface } from './gherkin.js';
|
|
3
|
+
export interface AcceptanceFieldInterface {
|
|
4
|
+
readonly kind: string;
|
|
5
|
+
readonly kinds: readonly AcceptanceKindType[];
|
|
6
|
+
readonly line: number;
|
|
7
|
+
readonly problem: string | undefined;
|
|
8
|
+
readonly reason: string;
|
|
9
|
+
}
|
|
10
|
+
export type AcceptanceKindType = (typeof acceptanceKinds)[number];
|
|
11
|
+
export type ScenarioStateType = 'без сценария' | 'все сняты' | 'с тестом' | 'только долг';
|
|
12
|
+
export interface StoryClassInterface {
|
|
13
|
+
readonly defects: readonly string[];
|
|
14
|
+
readonly kinds: readonly AcceptanceKindType[] | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface StoryInputInterface {
|
|
17
|
+
readonly config: ConfigInterface;
|
|
18
|
+
readonly location: string;
|
|
19
|
+
readonly text: string;
|
|
20
|
+
}
|
|
21
|
+
declare const acceptanceKinds: readonly ["e2e", "code-tests", "none"];
|
|
22
|
+
export declare const acceptanceFields: (text: string) => AcceptanceFieldInterface[];
|
|
23
|
+
export declare const storyLinks: (text: string, config: ConfigInterface) => (readonly [number, string])[];
|
|
24
|
+
export declare const storyClass: (input: StoryInputInterface) => StoryClassInterface;
|
|
25
|
+
export declare const scenarioState: (storyIds: readonly string[], ids: ReadonlyMap<string, readonly ScenarioInterface[]>, removed: ReadonlyMap<string, number>) => ScenarioStateType;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scenarioState = exports.storyClass = exports.storyLinks = exports.acceptanceFields = void 0;
|
|
4
|
+
const config_js_1 = require("./config.js");
|
|
5
|
+
const python_text_js_1 = require("./python-text.js");
|
|
6
|
+
const task_links_js_1 = require("./task-links.js");
|
|
7
|
+
const acceptanceField = /\*\*Acceptance tests:\*\*[\t ]*(?<value>.*?)(?=\s+·\s+\*\*|$)/gu;
|
|
8
|
+
const acceptanceKinds = ['e2e', 'code-tests', 'none'];
|
|
9
|
+
const legacyKinds = {
|
|
10
|
+
'n/a': 'none',
|
|
11
|
+
ниже: 'code-tests',
|
|
12
|
+
сценарий: 'e2e',
|
|
13
|
+
};
|
|
14
|
+
const kindNames = acceptanceKinds;
|
|
15
|
+
const isKind = (name) => kindNames.includes(name);
|
|
16
|
+
const partition = (text, separator) => {
|
|
17
|
+
const index = text.indexOf(separator);
|
|
18
|
+
return index === -1 ? [text, ''] : [text.slice(0, index), text.slice(index + separator.length)];
|
|
19
|
+
};
|
|
20
|
+
const namesOf = (kindPart) => kindPart.split(',').map((name) => name.trim());
|
|
21
|
+
const setProblem = (kindPart, kinds) => {
|
|
22
|
+
const field = `\`**Acceptance tests:** ${kindPart}\``;
|
|
23
|
+
if (new Set(kinds).size < kinds.length) {
|
|
24
|
+
return `${field}: класс в наборе дважды`;
|
|
25
|
+
}
|
|
26
|
+
return kinds.includes('none') && kinds.length > 1 ? `${field}: none в наборе с другим классом` : undefined;
|
|
27
|
+
};
|
|
28
|
+
const nameProblem = (kindPart, names, value) => {
|
|
29
|
+
for (const name of names) {
|
|
30
|
+
const renamed = legacyKinds[name];
|
|
31
|
+
if (renamed !== undefined) {
|
|
32
|
+
return `\`**Acceptance tests:** ${kindPart}\`: старое имя класса ${name} — теперь ${renamed}`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (names.every((name) => isKind(name))) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const shown = (0, python_text_js_1.pyRepr)([...value].slice(0, 60).join(''));
|
|
39
|
+
return `\`**Acceptance tests:**\` вне набора ${acceptanceKinds.join(' | ')} (через запятую): ${shown}`;
|
|
40
|
+
};
|
|
41
|
+
const reasonProblem = (kindPart, kinds, reason) => {
|
|
42
|
+
const isUnexplained = kinds.some((kind) => kind !== 'e2e') && reason === '';
|
|
43
|
+
return isUnexplained ? `\`**Acceptance tests:** ${kindPart}\` без причины после тире` : undefined;
|
|
44
|
+
};
|
|
45
|
+
const problemOf = (kindPart, reason, value) => {
|
|
46
|
+
const names = namesOf(kindPart);
|
|
47
|
+
return nameProblem(kindPart, names, value) ?? reasonProblem(kindPart, names, reason) ?? setProblem(kindPart, names);
|
|
48
|
+
};
|
|
49
|
+
const fieldOf = (rawValue, line) => {
|
|
50
|
+
const value = rawValue.trim();
|
|
51
|
+
const [kindPart, reasonPart] = partition(value, '—');
|
|
52
|
+
const kind = kindPart.trim();
|
|
53
|
+
const reason = reasonPart.trim();
|
|
54
|
+
const problem = problemOf(kind, reason, value);
|
|
55
|
+
const kinds = problem === undefined ? namesOf(kind).filter((name) => isKind(name)) : [];
|
|
56
|
+
return { kind, kinds, line, problem, reason };
|
|
57
|
+
};
|
|
58
|
+
const lineFields = (line, index) => [...line.matchAll(acceptanceField)]
|
|
59
|
+
.map((match) => fieldOf(match.groups?.value ?? '', index + 1));
|
|
60
|
+
const acceptanceFields = (text) => {
|
|
61
|
+
const [header] = partition(text, '\n## ');
|
|
62
|
+
return (0, python_text_js_1.splitLines)(header).flatMap((line, index) => lineFields(line, index));
|
|
63
|
+
};
|
|
64
|
+
exports.acceptanceFields = acceptanceFields;
|
|
65
|
+
const storyLinks = (text, config) => (0, python_text_js_1.splitLines)(text)
|
|
66
|
+
.flatMap((line, index) => [...line.matchAll(task_links_js_1.linkToken)]
|
|
67
|
+
.filter((match) => (0, config_js_1.isProjectCovered)(config, match.groups?.project ?? ''))
|
|
68
|
+
.map((match) => [index + 1, match[0].slice(1)]));
|
|
69
|
+
exports.storyLinks = storyLinks;
|
|
70
|
+
const repeatedField = (fields, line, location) => {
|
|
71
|
+
const lines = fields.map((item) => String(item.line)).join(', ');
|
|
72
|
+
return `поле \`**Acceptance tests:**\` в шапке ${fields.length} раза (строки ${lines}): ${location}:${line}`;
|
|
73
|
+
};
|
|
74
|
+
const contradiction = (input, field) => {
|
|
75
|
+
const [link] = (0, exports.storyLinks)(input.text, input.config);
|
|
76
|
+
return link === undefined || field.kinds.includes('e2e') ? [] : [`класс ${field.kind}, а story ссылается на сценарий @${link[1]}: ${input.location}:${link[0]}`];
|
|
77
|
+
};
|
|
78
|
+
const classOf = (input, field) => (field.problem === undefined ? { defects: contradiction(input, field), kinds: field.kinds } : { defects: [`${field.problem}: ${input.location}:${field.line}`], kinds: undefined });
|
|
79
|
+
const storyClass = (input) => {
|
|
80
|
+
const fields = (0, exports.acceptanceFields)(input.text);
|
|
81
|
+
const [field, second] = fields;
|
|
82
|
+
if (field === undefined) {
|
|
83
|
+
return { defects: [`закрытая story без поля \`**Acceptance tests:**\`: ${input.location}`], kinds: undefined };
|
|
84
|
+
}
|
|
85
|
+
return second === undefined
|
|
86
|
+
? classOf(input, field)
|
|
87
|
+
: { defects: [repeatedField(fields, second.line, input.location)], kinds: undefined };
|
|
88
|
+
};
|
|
89
|
+
exports.storyClass = storyClass;
|
|
90
|
+
const scenarioState = (storyIds, ids, removed) => {
|
|
91
|
+
const alive = storyIds.filter((id) => !removed.has(id)).flatMap((id) => ids.get(id)?.slice(0, 1) ?? []);
|
|
92
|
+
if (alive.some((scenario) => !scenario.tags.includes(config_js_1.markTodo))) {
|
|
93
|
+
return 'с тестом';
|
|
94
|
+
}
|
|
95
|
+
if (alive.length > 0) {
|
|
96
|
+
return 'только долг';
|
|
97
|
+
}
|
|
98
|
+
return storyIds.some((id) => removed.has(id)) ? 'все сняты' : 'без сценария';
|
|
99
|
+
};
|
|
100
|
+
exports.scenarioState = scenarioState;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type RecordType = Readonly<Record<string, unknown>>;
|
|
2
|
+
export declare const acceptanceKey = "acceptance_tests";
|
|
3
|
+
export declare const isRecord: (value: unknown) => value is RecordType;
|
|
4
|
+
export declare const parseBoard: (text: string) => unknown;
|
|
5
|
+
export declare const boardStatuses: (text: string, where: string) => Map<string, string>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.boardStatuses = exports.parseBoard = exports.isRecord = exports.acceptanceKey = void 0;
|
|
4
|
+
const yaml_1 = require("yaml");
|
|
5
|
+
const config_js_1 = require("./config.js");
|
|
6
|
+
const python_text_js_1 = require("./python-text.js");
|
|
7
|
+
const boardFormat = 6;
|
|
8
|
+
const epicFields = new Set(['retro', 'status', 'tasks', 'version']);
|
|
9
|
+
const legacyStatuses = {
|
|
10
|
+
contexted: 'in_progress',
|
|
11
|
+
drafted: 'ready',
|
|
12
|
+
'in-progress': 'in_progress',
|
|
13
|
+
'ready-for-dev': 'ready',
|
|
14
|
+
};
|
|
15
|
+
const epicKey = /^epic-\d+[a-z]?$/u;
|
|
16
|
+
const integer = /^[+-]?\d+$/u;
|
|
17
|
+
const yamlLogLevel = 'error';
|
|
18
|
+
exports.acceptanceKey = 'acceptance_tests';
|
|
19
|
+
const isRecord = (value) => {
|
|
20
|
+
const isObject = typeof value === 'object' && value !== null;
|
|
21
|
+
return isObject && !Array.isArray(value);
|
|
22
|
+
};
|
|
23
|
+
exports.isRecord = isRecord;
|
|
24
|
+
const parseBoard = (text) => (0, yaml_1.parse)(text, {
|
|
25
|
+
logLevel: yamlLogLevel,
|
|
26
|
+
maxAliasCount: -1,
|
|
27
|
+
uniqueKeys: false,
|
|
28
|
+
version: '1.1',
|
|
29
|
+
});
|
|
30
|
+
exports.parseBoard = parseBoard;
|
|
31
|
+
const isMissing = (value) => value === undefined || value === null;
|
|
32
|
+
const formatOf = (data, where) => {
|
|
33
|
+
const raw = isMissing(data.version) ? data.board_format : data.version;
|
|
34
|
+
const text = isMissing(raw) ? '1' : (0, python_text_js_1.pyText)(raw).trim();
|
|
35
|
+
if (!integer.test(text)) {
|
|
36
|
+
throw new config_js_1.ConfigError(`board ${where}: version: ${text} — ожидалось целое число версии формата`);
|
|
37
|
+
}
|
|
38
|
+
return Number(text);
|
|
39
|
+
};
|
|
40
|
+
const normalize = (value) => {
|
|
41
|
+
const status = (0, python_text_js_1.pyText)(value);
|
|
42
|
+
return legacyStatuses[status] ?? status;
|
|
43
|
+
};
|
|
44
|
+
const statusField = (value) => ('status' in value ? value.status : 'backlog');
|
|
45
|
+
const taskStatus = (value) => normalize((0, exports.isRecord)(value) ? statusField(value) : value);
|
|
46
|
+
const isEpicBlock = (block) => {
|
|
47
|
+
const keys = (0, exports.isRecord)(block) ? Object.keys(block) : undefined;
|
|
48
|
+
return keys?.every((key) => epicFields.has(key)) === true;
|
|
49
|
+
};
|
|
50
|
+
const tasksOf = (epic, block, where) => {
|
|
51
|
+
const tasks = block.tasks ?? {};
|
|
52
|
+
if (!(0, exports.isRecord)(tasks)) {
|
|
53
|
+
throw new config_js_1.ConfigError(`board ${where}: development_status[${epic}].tasks — ожидался маппинг задач`);
|
|
54
|
+
}
|
|
55
|
+
return tasks;
|
|
56
|
+
};
|
|
57
|
+
const epicTasks = (epic, block, where) => {
|
|
58
|
+
if (isMissing(block)) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
if (!isEpicBlock(block)) {
|
|
62
|
+
throw new config_js_1.ConfigError(`board ${where}: development_status[${epic}] — не блок эпика формата ${boardFormat}`);
|
|
63
|
+
}
|
|
64
|
+
return tasksOf(epic, block, where);
|
|
65
|
+
};
|
|
66
|
+
const isStory = (key) => !key.endsWith('-retrospective') && !epicKey.test(key);
|
|
67
|
+
const parseOrFail = (text, where) => {
|
|
68
|
+
try {
|
|
69
|
+
return (0, exports.parseBoard)(text) ?? {};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw new config_js_1.ConfigError(`board ${where}: невалидный YAML: ${(0, python_text_js_1.messageOf)(error)}`);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const loadBoard = (text, where) => {
|
|
76
|
+
const data = parseOrFail(text, where);
|
|
77
|
+
if (!(0, exports.isRecord)(data)) {
|
|
78
|
+
throw new config_js_1.ConfigError(`board ${where}: ожидался маппинг`);
|
|
79
|
+
}
|
|
80
|
+
return data;
|
|
81
|
+
};
|
|
82
|
+
const epicsOf = (data, where) => {
|
|
83
|
+
const format = formatOf(data, where);
|
|
84
|
+
if (format !== boardFormat) {
|
|
85
|
+
throw new config_js_1.ConfigError(`board ${where}: формат ${format}, инструмент понимает ${boardFormat}`);
|
|
86
|
+
}
|
|
87
|
+
const epics = data.development_status ?? {};
|
|
88
|
+
if (!(0, exports.isRecord)(epics)) {
|
|
89
|
+
throw new config_js_1.ConfigError(`board ${where}: development_status — ожидался маппинг эпиков`);
|
|
90
|
+
}
|
|
91
|
+
return epics;
|
|
92
|
+
};
|
|
93
|
+
const boardStatuses = (text, where) => {
|
|
94
|
+
const epics = epicsOf(loadBoard(text, where), where);
|
|
95
|
+
const tasks = Object.entries(epics).flatMap(([epic, block]) => Object.entries(epicTasks(epic, block, where)));
|
|
96
|
+
return new Map(tasks.filter(([key]) => isStory(key)).map(([key, value]) => [key, taskStatus(value)]));
|
|
97
|
+
};
|
|
98
|
+
exports.boardStatuses = boardStatuses;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ClassificationInterface, ScenariosInterface } from './classify.js';
|
|
2
|
+
import type { DeclaredInputInterface } from './declared.js';
|
|
3
|
+
import type { ProjectInterface } from './projects.js';
|
|
4
|
+
import type { ReportInterface } from './report.js';
|
|
5
|
+
export interface ClassifiedAllInterface {
|
|
6
|
+
readonly classified: readonly ClassifiedInterface[];
|
|
7
|
+
readonly defects: readonly string[];
|
|
8
|
+
readonly notes: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
export interface ClassifiedInterface extends ClassificationInterface {
|
|
11
|
+
readonly project: ProjectInterface;
|
|
12
|
+
}
|
|
13
|
+
export declare const noProjects = "\u043F\u0440\u043E\u0435\u043A\u0442\u043E\u0432, \u0447\u0435\u0439 board \u043E\u0431\u044A\u044F\u0432\u043B\u044F\u0435\u0442 \u044D\u0442\u043E\u0442 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439, \u043D\u0435\u0442";
|
|
14
|
+
export declare const classifyDeclared: (scenarios: ScenariosInterface, input: DeclaredInputInterface) => Promise<ClassifiedAllInterface>;
|
|
15
|
+
export declare const sourcesLine: (projects: readonly ProjectInterface[]) => string;
|
|
16
|
+
export declare const classesCommand: (input: DeclaredInputInterface, isListed: boolean) => Promise<ReportInterface>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.classesCommand = exports.sourcesLine = exports.classifyDeclared = exports.noProjects = void 0;
|
|
4
|
+
const classify_js_1 = require("./classify.js");
|
|
5
|
+
const config_js_1 = require("./config.js");
|
|
6
|
+
const declared_js_1 = require("./declared.js");
|
|
7
|
+
const features_js_1 = require("./features.js");
|
|
8
|
+
const projects_js_1 = require("./projects.js");
|
|
9
|
+
const removed_list_js_1 = require("./removed-list.js");
|
|
10
|
+
exports.noProjects = 'проектов, чей board объявляет этот репозиторий, нет';
|
|
11
|
+
const readScenarios = async (input) => {
|
|
12
|
+
const files = await (0, features_js_1.worktreeFeatures)(input.cwd, input.config);
|
|
13
|
+
const removed = await (0, removed_list_js_1.parseRemoved)(input.cwd, input.config.removed);
|
|
14
|
+
return { config: input.config, cwd: input.cwd, ids: (0, features_js_1.scenarioIds)(input.config, files), removed: removed.entries };
|
|
15
|
+
};
|
|
16
|
+
const classifyProject = async (scenarios, project) => {
|
|
17
|
+
const classification = await (0, classify_js_1.classify)(scenarios, project);
|
|
18
|
+
return { ...classification, project };
|
|
19
|
+
};
|
|
20
|
+
const classifyDeclared = async (scenarios, input) => {
|
|
21
|
+
const { declared, notes } = await (0, declared_js_1.declaredProjects)(input);
|
|
22
|
+
const results = await Promise.all(declared.map(async ({ defect, project }) => {
|
|
23
|
+
const item = await classifyProject(scenarios, project);
|
|
24
|
+
return { defects: [defect ?? [], item.defects].flat(), item };
|
|
25
|
+
}));
|
|
26
|
+
return { classified: results.map(({ item }) => item), defects: results.flatMap(({ defects }) => defects), notes };
|
|
27
|
+
};
|
|
28
|
+
exports.classifyDeclared = classifyDeclared;
|
|
29
|
+
const projectLine = ({ closed, counts, project }) => {
|
|
30
|
+
const e2e = `e2e ${counts.e2e} (с тестом ${counts['с тестом']}, только долг @${config_js_1.markTodo} `
|
|
31
|
+
+ `${counts['только долг']}, все сняты ${counts['все сняты']}, без сценария ${counts['без сценария']})`;
|
|
32
|
+
const others = `code-tests ${counts['code-tests']}, none ${counts.none}, без класса ${counts['без класса']}`;
|
|
33
|
+
return `${project.name} — закрытых ${closed}: ${e2e}, ${others}, знаменатель покрытия ${counts.e2e - counts['все сняты']}`;
|
|
34
|
+
};
|
|
35
|
+
const sourcesLine = (projects) => {
|
|
36
|
+
const sources = projects.map((project) => (0, projects_js_1.sourceOf)(project)).join('; ');
|
|
37
|
+
return `задачи: ${sources || 'нет'}`;
|
|
38
|
+
};
|
|
39
|
+
exports.sourcesLine = sourcesLine;
|
|
40
|
+
const classesCommand = async (input, isListed) => {
|
|
41
|
+
const scenarios = await readScenarios(input);
|
|
42
|
+
const { classified, defects, notes } = await (0, exports.classifyDeclared)(scenarios, input);
|
|
43
|
+
const parts = classified.map((item) => projectLine(item)).join(' · ');
|
|
44
|
+
const transfers = isListed ? classified.flatMap((item) => item.transfers) : [];
|
|
45
|
+
const summary = [`классы закрытых story: ${parts || exports.noProjects}`, (0, exports.sourcesLine)(classified.map((item) => item.project))];
|
|
46
|
+
return { defects, notes, summary: [...summary, ...transfers].join('\n') };
|
|
47
|
+
};
|
|
48
|
+
exports.classesCommand = classesCommand;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AcceptanceKindType, ScenarioStateType } from './acceptance-class.js';
|
|
2
|
+
import type { ConfigInterface } from './config.js';
|
|
3
|
+
import type { ScenarioInterface } from './gherkin.js';
|
|
4
|
+
import type { ProjectInterface } from './projects.js';
|
|
5
|
+
export interface ClassificationInterface {
|
|
6
|
+
readonly closed: number;
|
|
7
|
+
readonly counts: CountsType;
|
|
8
|
+
readonly defects: readonly string[];
|
|
9
|
+
readonly open: readonly OpenStoryInterface[];
|
|
10
|
+
readonly stories: readonly ScenarioStoryInterface[];
|
|
11
|
+
readonly transfers: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export interface OpenStoryInterface {
|
|
14
|
+
readonly file: string | undefined;
|
|
15
|
+
readonly ids: readonly string[];
|
|
16
|
+
readonly key: string;
|
|
17
|
+
readonly kinds: readonly AcceptanceKindType[] | undefined;
|
|
18
|
+
readonly status: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ScenariosInterface {
|
|
21
|
+
readonly config: ConfigInterface;
|
|
22
|
+
readonly cwd: string;
|
|
23
|
+
readonly ids: ReadonlyMap<string, readonly ScenarioInterface[]>;
|
|
24
|
+
readonly removed: ReadonlyMap<string, number>;
|
|
25
|
+
}
|
|
26
|
+
export interface ScenarioStoryInterface {
|
|
27
|
+
readonly file: string;
|
|
28
|
+
readonly ids: readonly string[];
|
|
29
|
+
readonly key: string;
|
|
30
|
+
readonly state: ScenarioStateType;
|
|
31
|
+
}
|
|
32
|
+
type CountsType = Readonly<Record<CountType, number>>;
|
|
33
|
+
type CountType = 'без класса' | AcceptanceKindType | ScenarioStateType;
|
|
34
|
+
export declare const classify: (scenarios: ScenariosInterface, project: ProjectInterface) => Promise<ClassificationInterface>;
|
|
35
|
+
export {};
|