- 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>
160 lines
5.4 KiB
JavaScript
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();
|