Files
sam-hotfix/e2e/runner/revert-hard-actions.js
김보곤 67d0a4c2fd feat: E2E 시나리오 생성기 및 감사 스크립트 17종 추가
- gen-*.js: 시나리오 자동 생성기 12종 (CRUD, edge, a11y, perf 등)
- search-*.js: 검색/버튼 감사 수집기 3종
- revert-hard-actions.js: 하드 액션 복원 유틸
- _gen_writer.py: 생성기 보조 스크립트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:59:15 +09:00

160 lines
5.4 KiB
JavaScript

/**
* revert-hard-actions.js
*
* fix-scenario-quality.js가 적용한 hard action을 soft action으로 되돌림.
* - DELETE click → click_if_exists
* - DELETE click_dialog_confirm → click_if_exists (target 복원)
* - UPDATE fill (click_if_exists에서 변경된 것) → click_if_exists (value 제거)
* - CREATE fill_form (fields array가 있지만 원래 fill_form이 아닌 것) → click_if_exists
*
* Usage: node e2e/runner/revert-hard-actions.js [--dry-run]
*/
const fs = require('fs');
const path = require('path');
const SCENARIOS_DIR = path.join(__dirname, '..', 'scenarios');
const DRY_RUN = process.argv.includes('--dry-run');
const SKIP_PATTERNS = [
'_backup_before_enhance',
'_global-',
'_templates',
'pdf-download-test.json',
'.claude'
];
const changeLog = [];
function main() {
console.log('='.repeat(60));
console.log(' Revert Hard Actions → Soft Actions');
console.log(' ' + (DRY_RUN ? '[DRY RUN]' : '[LIVE MODE]'));
console.log('='.repeat(60));
console.log();
const files = fs.readdirSync(SCENARIOS_DIR)
.filter(f => f.endsWith('.json'))
.filter(f => !SKIP_PATTERNS.some(p => f.includes(p)))
.sort();
let filesModified = 0;
for (const file of files) {
const filePath = path.join(SCENARIOS_DIR, file);
let content;
try {
content = fs.readFileSync(filePath, 'utf-8');
} catch (err) {
continue;
}
let scenario;
try {
scenario = JSON.parse(content);
} catch (err) {
continue;
}
const steps = scenario.steps;
if (!steps || !Array.isArray(steps)) continue;
const beforeCount = changeLog.length;
for (const step of steps) {
// Revert 1: DELETE click → click_if_exists
if (step.phase === 'DELETE' && step.action === 'click') {
changeLog.push({
file, stepId: step.id, stepName: step.name,
before: 'action: "click"',
after: 'action: "click_if_exists"'
});
step.action = 'click_if_exists';
}
// Revert 2: DELETE click_dialog_confirm → click_if_exists (add target)
if (step.phase === 'DELETE' && step.action === 'click_dialog_confirm') {
const dialogTarget = "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('확인'), button:has-text('확인')";
changeLog.push({
file, stepId: step.id, stepName: step.name,
before: 'action: "click_dialog_confirm"',
after: `action: "click_if_exists", target restored`
});
step.action = 'click_if_exists';
if (!step.target) {
step.target = dialogTarget;
}
}
// Revert 3: UPDATE fill on input/textarea → click_if_exists (remove value)
if (step.phase === 'UPDATE' && step.action === 'fill') {
const target = (step.target || '').toLowerCase();
const isInputTarget = target.includes('input[') || target.includes('input:') ||
target.includes('textarea[') || target.includes('textarea:') ||
target.includes('select[') || target.includes('select:');
// Only revert fill steps that target input/textarea (these were changed from click_if_exists)
if (isInputTarget) {
changeLog.push({
file, stepId: step.id, stepName: step.name,
before: `action: "fill", value: "${step.value}"`,
after: 'action: "click_if_exists" (value removed)'
});
step.action = 'click_if_exists';
delete step.value;
}
}
// Revert 4: CREATE fill_form (where fields don't match actual UI) → click_if_exists
// Only revert if step has fields array AND phase is CREATE
// We check if the step name suggests it's a form input step
if (step.phase === 'CREATE' && step.action === 'fill_form' && Array.isArray(step.fields)) {
// Check if this was changed by fix-scenario-quality.js (Issue 3)
// We can identify these by checking if the step was likely not originally fill_form
// Heuristic: if step name includes "정보 입력" and fields exist, it was likely changed
const nameHint = (step.name || '').toLowerCase();
const wasLikelyChanged = nameHint.includes('정보 입력') || nameHint.includes('정보입력');
if (wasLikelyChanged) {
changeLog.push({
file, stepId: step.id, stepName: step.name,
before: 'action: "fill_form" with fields',
after: 'action: "fill_form" (kept - fields exist)'
});
// Actually keep fill_form for CREATE - just log it. The fields need fixing, not the action.
}
}
}
const changesInFile = changeLog.length - beforeCount;
if (changesInFile > 0) {
filesModified++;
console.log(` [FIX] ${file} - ${changesInFile} change(s)`);
if (!DRY_RUN) {
const output = JSON.stringify(scenario, null, 2);
fs.writeFileSync(filePath, output + '\n', 'utf-8');
}
}
}
console.log();
console.log('='.repeat(60));
console.log(` Files modified: ${filesModified}`);
console.log(` Total changes: ${changeLog.length}`);
console.log('='.repeat(60));
console.log();
if (changeLog.length > 0) {
for (const c of changeLog) {
console.log(` ${c.file} (step ${c.stepId}: "${c.stepName}")`);
console.log(` ${c.before}${c.after}`);
}
console.log();
}
if (DRY_RUN && changeLog.length > 0) {
console.log(' *** DRY RUN: Run without --dry-run to apply. ***');
}
}
main();