@qaguardian/playwright-score 0.1.0
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/LICENSE +21 -0
- package/METHODOLOGY.md +178 -0
- package/README.md +109 -0
- package/bin/playwright-score.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +113 -0
- package/dist/eslint-runner.d.ts +10 -0
- package/dist/eslint-runner.js +120 -0
- package/dist/formatters/json.d.ts +2 -0
- package/dist/formatters/json.js +3 -0
- package/dist/formatters/markdown.d.ts +2 -0
- package/dist/formatters/markdown.js +26 -0
- package/dist/formatters/sarif.d.ts +3 -0
- package/dist/formatters/sarif.js +55 -0
- package/dist/formatters/text.d.ts +2 -0
- package/dist/formatters/text.js +32 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +95 -0
- package/dist/metrics.d.ts +17 -0
- package/dist/metrics.js +79 -0
- package/dist/profiles.d.ts +15 -0
- package/dist/profiles.js +130 -0
- package/dist/rules/guardian-plugin.d.ts +29 -0
- package/dist/rules/guardian-plugin.js +285 -0
- package/dist/score-engine.d.ts +29 -0
- package/dist/score-engine.js +114 -0
- package/dist/sloc.d.ts +4 -0
- package/dist/sloc.js +38 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.js +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function formatText(result) {
|
|
2
|
+
const lines = [];
|
|
3
|
+
lines.push(`Playwright Spec Score ${result.scoreVersion} · profile=${result.profile}`);
|
|
4
|
+
lines.push(`Score: ${result.score}/100 (${result.grade}) · ${result.pass ? 'PASS' : 'FAIL'} (threshold ${result.threshold})`);
|
|
5
|
+
lines.push('');
|
|
6
|
+
lines.push('Dimensions:');
|
|
7
|
+
for (const [k, v] of Object.entries(result.dimensions)) {
|
|
8
|
+
if (v === undefined)
|
|
9
|
+
continue;
|
|
10
|
+
lines.push(` ${k}: ${v}`);
|
|
11
|
+
}
|
|
12
|
+
lines.push('');
|
|
13
|
+
lines.push(`Summary: files=${result.summary.files} tests=${result.summary.tests} sloc=${result.summary.sloc} ` +
|
|
14
|
+
`findings=${result.summary.findings} (errors=${result.summary.errors} warnings=${result.summary.warnings}) ` +
|
|
15
|
+
`locators native/raw=${result.summary.nativeLocators}/${result.summary.rawLocators}`);
|
|
16
|
+
if (result.findings.length) {
|
|
17
|
+
lines.push('');
|
|
18
|
+
lines.push('Findings:');
|
|
19
|
+
for (const f of result.findings.slice(0, 50)) {
|
|
20
|
+
const loc = f.line ? `:${f.line}` : '';
|
|
21
|
+
lines.push(` [${f.severity}] ${f.rule} ${pathBasename(f.file)}${loc} — ${f.message}`);
|
|
22
|
+
}
|
|
23
|
+
if (result.findings.length > 50) {
|
|
24
|
+
lines.push(` … and ${result.findings.length - 50} more`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return lines.join('\n');
|
|
28
|
+
}
|
|
29
|
+
function pathBasename(p) {
|
|
30
|
+
const parts = p.split(/[/\\]/);
|
|
31
|
+
return parts[parts.length - 1] ?? p;
|
|
32
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ScoreOptions, ScoreResult } from './types.js';
|
|
2
|
+
export type { ScoreResult, ScoreOptions, Finding, ProfileName } from './types.js';
|
|
3
|
+
export { computeScore, SQS_V1, locatorsScore, gradeFromScore } from './score-engine.js';
|
|
4
|
+
export { countSloc } from './sloc.js';
|
|
5
|
+
export { countLocators, analyzeSource } from './metrics.js';
|
|
6
|
+
export { formatJson } from './formatters/json.js';
|
|
7
|
+
export { formatText } from './formatters/text.js';
|
|
8
|
+
export { formatMarkdown } from './formatters/markdown.js';
|
|
9
|
+
export { formatSarif } from './formatters/sarif.js';
|
|
10
|
+
/**
|
|
11
|
+
* Score Playwright spec files. Deterministic and AI-free (sqs-v1).
|
|
12
|
+
*/
|
|
13
|
+
export declare function scorePaths(options: ScoreOptions): Promise<ScoreResult>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { globSync } from 'glob';
|
|
4
|
+
import { runEslint } from './eslint-runner.js';
|
|
5
|
+
import { analyzeSource } from './metrics.js';
|
|
6
|
+
import { DEFAULT_THRESHOLDS } from './profiles.js';
|
|
7
|
+
import { computeScore } from './score-engine.js';
|
|
8
|
+
export { computeScore, SQS_V1, locatorsScore, gradeFromScore } from './score-engine.js';
|
|
9
|
+
export { countSloc } from './sloc.js';
|
|
10
|
+
export { countLocators, analyzeSource } from './metrics.js';
|
|
11
|
+
export { formatJson } from './formatters/json.js';
|
|
12
|
+
export { formatText } from './formatters/text.js';
|
|
13
|
+
export { formatMarkdown } from './formatters/markdown.js';
|
|
14
|
+
export { formatSarif } from './formatters/sarif.js';
|
|
15
|
+
const SPEC_GLOBS = ['**/*.{spec,test}.{ts,tsx,js,jsx}', '**/*.spec.ts', '**/*.test.ts'];
|
|
16
|
+
function expandPaths(inputs, cwd) {
|
|
17
|
+
const files = new Set();
|
|
18
|
+
for (const input of inputs) {
|
|
19
|
+
const abs = path.isAbsolute(input) ? input : path.resolve(cwd, input);
|
|
20
|
+
if (fs.existsSync(abs) && fs.statSync(abs).isFile()) {
|
|
21
|
+
files.add(abs);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
|
|
25
|
+
for (const g of SPEC_GLOBS) {
|
|
26
|
+
for (const f of globSync(g, { cwd: abs, absolute: true, nodir: true })) {
|
|
27
|
+
files.add(path.resolve(f));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
// treat as glob
|
|
33
|
+
for (const f of globSync(input, { cwd, absolute: true, nodir: true })) {
|
|
34
|
+
files.add(path.resolve(f));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return [...files].sort();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Score Playwright spec files. Deterministic and AI-free (sqs-v1).
|
|
41
|
+
*/
|
|
42
|
+
export async function scorePaths(options) {
|
|
43
|
+
const cwd = options.cwd ?? process.cwd();
|
|
44
|
+
const profile = options.profile ?? 'standard';
|
|
45
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLDS[profile] ?? 80;
|
|
46
|
+
const files = expandPaths(options.paths, cwd);
|
|
47
|
+
if (files.length === 0) {
|
|
48
|
+
return computeScore({
|
|
49
|
+
profile,
|
|
50
|
+
threshold,
|
|
51
|
+
findings: [
|
|
52
|
+
{
|
|
53
|
+
rule: 'playwright-score/no-files',
|
|
54
|
+
severity: 'error',
|
|
55
|
+
message: 'No Playwright spec files matched the given paths',
|
|
56
|
+
file: cwd,
|
|
57
|
+
dimension: 'structure',
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
sloc: 1,
|
|
61
|
+
files: 0,
|
|
62
|
+
tests: 0,
|
|
63
|
+
nativeLocators: 0,
|
|
64
|
+
rawLocators: 0,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const eslintFindings = await runEslint({ files, profile, cwd });
|
|
68
|
+
let totalSloc = 0;
|
|
69
|
+
let totalTests = 0;
|
|
70
|
+
let native = 0;
|
|
71
|
+
let raw = 0;
|
|
72
|
+
const metricFindings = [];
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
const source = fs.readFileSync(file, 'utf8');
|
|
75
|
+
const m = analyzeSource(source, file);
|
|
76
|
+
totalSloc += m.sloc;
|
|
77
|
+
totalTests += m.tests;
|
|
78
|
+
native += m.locators.native;
|
|
79
|
+
raw += m.locators.raw;
|
|
80
|
+
// metrics/no-empty-test duplicates guardian/require-expect under guardian — keep both mild
|
|
81
|
+
metricFindings.push(...m.findings);
|
|
82
|
+
}
|
|
83
|
+
// Dedupe empty-test style: if guardian require-expect already fired, still ok
|
|
84
|
+
const findings = [...eslintFindings, ...metricFindings];
|
|
85
|
+
return computeScore({
|
|
86
|
+
profile,
|
|
87
|
+
threshold,
|
|
88
|
+
findings,
|
|
89
|
+
sloc: Math.max(totalSloc, 1),
|
|
90
|
+
files: files.length,
|
|
91
|
+
tests: totalTests,
|
|
92
|
+
nativeLocators: native,
|
|
93
|
+
rawLocators: raw,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Finding, LocatorCounts } from './types.js';
|
|
2
|
+
/** Count native vs raw locator calls (simple statistical). */
|
|
3
|
+
export declare function countLocators(source: string): LocatorCounts;
|
|
4
|
+
/** Count test( / test.only( / test.skip( style declarations (approx). */
|
|
5
|
+
export declare function countTests(source: string): number;
|
|
6
|
+
/**
|
|
7
|
+
* Detect test callbacks that appear to have zero expect( calls.
|
|
8
|
+
* Heuristic: for each test( ... async (...) => { body }, check body for expect.
|
|
9
|
+
*/
|
|
10
|
+
export declare function findEmptyExpectTests(source: string, file: string): Finding[];
|
|
11
|
+
export declare function findOversizedFile(source: string, file: string, maxSloc?: number): Finding[];
|
|
12
|
+
export declare function analyzeSource(source: string, file: string): {
|
|
13
|
+
sloc: number;
|
|
14
|
+
tests: number;
|
|
15
|
+
locators: LocatorCounts;
|
|
16
|
+
findings: Finding[];
|
|
17
|
+
};
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { countSloc } from './sloc.js';
|
|
2
|
+
const NATIVE_RE = /\.\s*getBy(?:Role|Label|TestId|Text|Placeholder)\s*\(/g;
|
|
3
|
+
const RAW_RE = /(?:page|frame)\s*\.\s*locator\s*\(/g;
|
|
4
|
+
/** Count native vs raw locator calls (simple statistical). */
|
|
5
|
+
export function countLocators(source) {
|
|
6
|
+
const native = [...source.matchAll(NATIVE_RE)].length;
|
|
7
|
+
const raw = [...source.matchAll(RAW_RE)].length;
|
|
8
|
+
return { native, raw };
|
|
9
|
+
}
|
|
10
|
+
/** Count test( / test.only( / test.skip( style declarations (approx). */
|
|
11
|
+
export function countTests(source) {
|
|
12
|
+
const re = /\btest(?:\.(?:only|skip|fix|slow))?\s*\(/g;
|
|
13
|
+
// Exclude test.describe, test.step, test.before*, test.after*, test.use
|
|
14
|
+
let count = 0;
|
|
15
|
+
let m;
|
|
16
|
+
const exclude = /\btest\.(?:describe|step|beforeEach|afterEach|beforeAll|afterAll|use|setTimeout|info|expect)\b/;
|
|
17
|
+
while ((m = re.exec(source)) !== null) {
|
|
18
|
+
const start = Math.max(0, m.index - 0);
|
|
19
|
+
const snippet = source.slice(start, start + 40);
|
|
20
|
+
if (exclude.test(snippet.split('(')[0] + '(')) {
|
|
21
|
+
// more precise: check the matched prefix
|
|
22
|
+
}
|
|
23
|
+
const before = source.slice(Math.max(0, m.index - 1), m.index + 20);
|
|
24
|
+
if (/\btest\.(describe|step|beforeEach|afterEach|beforeAll|afterAll|use|setTimeout|info)\b/.test(before)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
// test.expect is rare as call form
|
|
28
|
+
if (/\btest\.expect\b/.test(before))
|
|
29
|
+
continue;
|
|
30
|
+
count++;
|
|
31
|
+
}
|
|
32
|
+
return count;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Detect test callbacks that appear to have zero expect( calls.
|
|
36
|
+
* Heuristic: for each test( ... async (...) => { body }, check body for expect.
|
|
37
|
+
*/
|
|
38
|
+
export function findEmptyExpectTests(source, file) {
|
|
39
|
+
const findings = [];
|
|
40
|
+
// Rough scan: if file has test( but no expect( at all
|
|
41
|
+
const hasTest = /\btest(?:\.(?:only|skip|fix))?\s*\(/.test(source);
|
|
42
|
+
const hasExpect = /\bexpect\s*\(/.test(source);
|
|
43
|
+
if (hasTest && !hasExpect) {
|
|
44
|
+
findings.push({
|
|
45
|
+
rule: 'metrics/no-empty-test',
|
|
46
|
+
severity: 'error',
|
|
47
|
+
message: 'Test file has test() but no expect() assertions',
|
|
48
|
+
file,
|
|
49
|
+
dimension: 'assertions',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return findings;
|
|
53
|
+
}
|
|
54
|
+
export function findOversizedFile(source, file, maxSloc = 400) {
|
|
55
|
+
const sloc = countSloc(source);
|
|
56
|
+
if (sloc > maxSloc) {
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
rule: 'metrics/oversized-file',
|
|
60
|
+
severity: 'warning',
|
|
61
|
+
message: `File has ${sloc} SLOC (limit ${maxSloc})`,
|
|
62
|
+
file,
|
|
63
|
+
dimension: 'structure',
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
export function analyzeSource(source, file) {
|
|
70
|
+
return {
|
|
71
|
+
sloc: countSloc(source),
|
|
72
|
+
tests: countTests(source),
|
|
73
|
+
locators: countLocators(source),
|
|
74
|
+
findings: [
|
|
75
|
+
...findEmptyExpectTests(source, file),
|
|
76
|
+
...findOversizedFile(source, file),
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DimensionName, ProfileName } from './types.js';
|
|
2
|
+
export declare const PROFILE_WEIGHTS: Record<ProfileName, Partial<Record<DimensionName, number>>>;
|
|
3
|
+
export declare const DEFAULT_THRESHOLDS: Record<ProfileName, number>;
|
|
4
|
+
/** Map eslint ruleId → dimension + whether report-only for scoring */
|
|
5
|
+
export interface RuleMapping {
|
|
6
|
+
dimension: DimensionName;
|
|
7
|
+
/** Override severity for scoring units; undefined keeps ESLint severity */
|
|
8
|
+
severityOverride?: 'error' | 'warning' | 'info';
|
|
9
|
+
reportOnly?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* ESLint rule → dimension map (plugin rule ids without prefix handled in runner).
|
|
13
|
+
*/
|
|
14
|
+
export declare const ESLINT_RULE_MAP: Record<string, RuleMapping>;
|
|
15
|
+
export declare function mapRule(ruleId: string): RuleMapping;
|
package/dist/profiles.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export const PROFILE_WEIGHTS = {
|
|
2
|
+
standard: {
|
|
3
|
+
playwrightHygiene: 40,
|
|
4
|
+
assertions: 25,
|
|
5
|
+
locators: 20,
|
|
6
|
+
structure: 15,
|
|
7
|
+
},
|
|
8
|
+
guardian: {
|
|
9
|
+
playwrightHygiene: 30,
|
|
10
|
+
assertions: 15,
|
|
11
|
+
locators: 15,
|
|
12
|
+
structure: 15,
|
|
13
|
+
guardianConventions: 25,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
export const DEFAULT_THRESHOLDS = {
|
|
17
|
+
standard: 80,
|
|
18
|
+
guardian: 75,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* ESLint rule → dimension map (plugin rule ids without prefix handled in runner).
|
|
22
|
+
*/
|
|
23
|
+
export const ESLINT_RULE_MAP = {
|
|
24
|
+
// Hygiene
|
|
25
|
+
'playwright/no-wait-for-timeout': { dimension: 'playwrightHygiene' },
|
|
26
|
+
'playwright/no-networkidle': { dimension: 'playwrightHygiene' },
|
|
27
|
+
'playwright/no-force-option': { dimension: 'playwrightHygiene' },
|
|
28
|
+
'playwright/no-wait-for-selector': { dimension: 'playwrightHygiene' },
|
|
29
|
+
'playwright/no-wait-for-navigation': { dimension: 'playwrightHygiene' },
|
|
30
|
+
'playwright/missing-playwright-await': { dimension: 'playwrightHygiene' },
|
|
31
|
+
'playwright/no-element-handle': { dimension: 'playwrightHygiene' },
|
|
32
|
+
'playwright/no-eval': { dimension: 'playwrightHygiene' },
|
|
33
|
+
'playwright/no-conditional-in-test': {
|
|
34
|
+
dimension: 'playwrightHygiene',
|
|
35
|
+
severityOverride: 'warning',
|
|
36
|
+
},
|
|
37
|
+
'playwright/no-conditional-expect': {
|
|
38
|
+
dimension: 'playwrightHygiene',
|
|
39
|
+
severityOverride: 'warning',
|
|
40
|
+
},
|
|
41
|
+
'playwright/no-page-pause': { dimension: 'playwrightHygiene' },
|
|
42
|
+
'playwright/no-useless-await': {
|
|
43
|
+
dimension: 'playwrightHygiene',
|
|
44
|
+
severityOverride: 'warning',
|
|
45
|
+
},
|
|
46
|
+
// Assertions
|
|
47
|
+
'playwright/expect-expect': { dimension: 'assertions' },
|
|
48
|
+
'playwright/prefer-web-first-assertions': {
|
|
49
|
+
dimension: 'assertions',
|
|
50
|
+
severityOverride: 'warning',
|
|
51
|
+
},
|
|
52
|
+
'playwright/valid-expect': { dimension: 'assertions' },
|
|
53
|
+
'playwright/no-standalone-expect': { dimension: 'assertions' },
|
|
54
|
+
// Locators — findings for repair, reportOnly so ratio owns locators score
|
|
55
|
+
'playwright/no-raw-locators': {
|
|
56
|
+
dimension: 'locators',
|
|
57
|
+
reportOnly: true,
|
|
58
|
+
severityOverride: 'warning',
|
|
59
|
+
},
|
|
60
|
+
'playwright/prefer-native-locators': {
|
|
61
|
+
dimension: 'locators',
|
|
62
|
+
reportOnly: true,
|
|
63
|
+
severityOverride: 'warning',
|
|
64
|
+
},
|
|
65
|
+
'playwright/prefer-locator': {
|
|
66
|
+
dimension: 'locators',
|
|
67
|
+
reportOnly: true,
|
|
68
|
+
severityOverride: 'info',
|
|
69
|
+
},
|
|
70
|
+
// Structure
|
|
71
|
+
'playwright/no-focused-test': { dimension: 'structure' },
|
|
72
|
+
'playwright/no-skipped-test': {
|
|
73
|
+
dimension: 'structure',
|
|
74
|
+
severityOverride: 'warning',
|
|
75
|
+
},
|
|
76
|
+
'playwright/max-nested-describe': {
|
|
77
|
+
dimension: 'structure',
|
|
78
|
+
severityOverride: 'warning',
|
|
79
|
+
},
|
|
80
|
+
'playwright/require-top-level-describe': {
|
|
81
|
+
dimension: 'structure',
|
|
82
|
+
severityOverride: 'warning',
|
|
83
|
+
},
|
|
84
|
+
// Guardian custom rules
|
|
85
|
+
'guardian/no-wait-for-load-state': { dimension: 'guardianConventions' },
|
|
86
|
+
'guardian/no-date-now-id': { dimension: 'guardianConventions' },
|
|
87
|
+
'guardian/require-expect': { dimension: 'guardianConventions' },
|
|
88
|
+
'guardian/no-hardcoded-secrets': {
|
|
89
|
+
dimension: 'guardianConventions',
|
|
90
|
+
severityOverride: 'warning',
|
|
91
|
+
},
|
|
92
|
+
'guardian/require-test-step': {
|
|
93
|
+
dimension: 'guardianConventions',
|
|
94
|
+
severityOverride: 'warning',
|
|
95
|
+
},
|
|
96
|
+
'guardian/one-describe-one-test': {
|
|
97
|
+
dimension: 'guardianConventions',
|
|
98
|
+
severityOverride: 'warning',
|
|
99
|
+
},
|
|
100
|
+
'guardian/no-generic-long-timeout': {
|
|
101
|
+
dimension: 'guardianConventions',
|
|
102
|
+
severityOverride: 'warning',
|
|
103
|
+
},
|
|
104
|
+
'guardian/prefer-uuid': {
|
|
105
|
+
dimension: 'guardianConventions',
|
|
106
|
+
severityOverride: 'warning',
|
|
107
|
+
},
|
|
108
|
+
'guardian/fixtures-import': {
|
|
109
|
+
dimension: 'guardianConventions',
|
|
110
|
+
severityOverride: 'info',
|
|
111
|
+
reportOnly: true,
|
|
112
|
+
},
|
|
113
|
+
// Metrics injected as synthetic findings
|
|
114
|
+
'metrics/no-empty-test': { dimension: 'assertions' },
|
|
115
|
+
'metrics/oversized-file': {
|
|
116
|
+
dimension: 'structure',
|
|
117
|
+
severityOverride: 'warning',
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
export function mapRule(ruleId) {
|
|
121
|
+
if (ESLINT_RULE_MAP[ruleId])
|
|
122
|
+
return ESLINT_RULE_MAP[ruleId];
|
|
123
|
+
if (ruleId.startsWith('guardian/')) {
|
|
124
|
+
return { dimension: 'guardianConventions', severityOverride: 'warning' };
|
|
125
|
+
}
|
|
126
|
+
if (ruleId.startsWith('playwright/')) {
|
|
127
|
+
return { dimension: 'playwrightHygiene', severityOverride: 'warning' };
|
|
128
|
+
}
|
|
129
|
+
return { dimension: 'playwrightHygiene', severityOverride: 'info', reportOnly: true };
|
|
130
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardian house-style ESLint rules for profile=guardian.
|
|
3
|
+
* Embedded in playwright-score — not claimed as official Playwright doctrine.
|
|
4
|
+
*/
|
|
5
|
+
import type { Rule } from 'eslint';
|
|
6
|
+
export declare const guardianPlugin: {
|
|
7
|
+
meta: {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
};
|
|
11
|
+
rules: {
|
|
12
|
+
'no-wait-for-load-state': Rule.RuleModule;
|
|
13
|
+
'no-date-now-id': Rule.RuleModule;
|
|
14
|
+
'no-hardcoded-secrets': Rule.RuleModule;
|
|
15
|
+
'no-generic-long-timeout': Rule.RuleModule;
|
|
16
|
+
'one-describe-one-test': Rule.RuleModule;
|
|
17
|
+
'require-test-step': Rule.RuleModule;
|
|
18
|
+
'require-expect': Rule.RuleModule;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export declare const guardianRuleConfigs: {
|
|
22
|
+
readonly 'guardian/no-wait-for-load-state': "error";
|
|
23
|
+
readonly 'guardian/no-date-now-id': "error";
|
|
24
|
+
readonly 'guardian/no-hardcoded-secrets': "warn";
|
|
25
|
+
readonly 'guardian/no-generic-long-timeout': "warn";
|
|
26
|
+
readonly 'guardian/one-describe-one-test': "warn";
|
|
27
|
+
readonly 'guardian/require-test-step': "warn";
|
|
28
|
+
readonly 'guardian/require-expect': "error";
|
|
29
|
+
};
|