Skip to content

Commit 7ebf5af

Browse files
committed
Add local/inline test-content.js for PR review automation tests
1 parent fc8efa0 commit 7ebf5af

File tree

1 file changed

+305
-0
lines changed

1 file changed

+305
-0
lines changed
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
6+
// Dynamic path resolution
7+
const scriptDir = __dirname;
8+
const ruleParserPath = path.join(scriptDir, 'rule-parser');
9+
const styleRulesPath = path.join(scriptDir, 'style-rules.yml');
10+
11+
/**
12+
* Flexible content tester for Strapi 12 Rules
13+
* Usage:
14+
* node test-content.js --file path/to/file.md
15+
* node test-content.js --text "Some text to test"
16+
* node test-content.js --stdin (then paste content)
17+
*/
18+
class FlexibleContentTester {
19+
constructor() {
20+
try {
21+
const Strapi12RulesParser = require(ruleParserPath);
22+
this.ruleParser = new Strapi12RulesParser(styleRulesPath);
23+
console.log('✅ Strapi 12 Rules loaded successfully');
24+
} catch (error) {
25+
console.error('❌ Failed to load rules:', error.message);
26+
process.exit(1);
27+
}
28+
}
29+
30+
async testFile(filePath) {
31+
console.log('🎯 File Validation Test');
32+
console.log('======================');
33+
console.log(`📄 File: ${filePath}`);
34+
console.log('');
35+
36+
if (!fs.existsSync(filePath)) {
37+
console.error(`❌ File not found: ${filePath}`);
38+
return;
39+
}
40+
41+
const content = fs.readFileSync(filePath, 'utf8');
42+
await this.validateContent(content, filePath);
43+
}
44+
45+
async testText(text) {
46+
console.log('🎯 Text Validation Test');
47+
console.log('======================');
48+
console.log(`📝 Content length: ${text.length} characters`);
49+
console.log(`📄 Lines: ${text.split('\n').length}`);
50+
console.log('');
51+
52+
await this.validateContent(text, '<text-input>');
53+
}
54+
55+
async testStdin() {
56+
console.log('🎯 Interactive Text Validation');
57+
console.log('==============================');
58+
console.log('📝 Paste your content below (Ctrl+D when finished):');
59+
console.log('');
60+
61+
let content = '';
62+
process.stdin.setEncoding('utf8');
63+
64+
for await (const chunk of process.stdin) {
65+
content += chunk;
66+
}
67+
68+
if (content.trim() === '') {
69+
console.log('❌ No content provided');
70+
return;
71+
}
72+
73+
console.log(`✅ Content received (${content.length} characters)`);
74+
console.log('');
75+
await this.validateContent(content, '<stdin>');
76+
}
77+
78+
async validateContent(content, source) {
79+
try {
80+
const allRules = this.ruleParser.getAllRules();
81+
console.log(`🔍 Applying ${allRules.length} validation rules...`);
82+
console.log('');
83+
84+
let totalIssues = 0;
85+
const issuesByRule = {};
86+
const issuesBySeverity = { error: [], warning: [], suggestion: [] };
87+
88+
// Apply all rules
89+
allRules.forEach(rule => {
90+
try {
91+
const issues = rule.validator(content, source);
92+
93+
if (issues.length > 0) {
94+
issuesByRule[rule.id] = {
95+
rule: rule,
96+
issues: issues
97+
};
98+
99+
issues.forEach(issue => {
100+
issue.ruleId = rule.id;
101+
issue.ruleDescription = rule.description;
102+
totalIssues++;
103+
104+
if (issuesBySeverity[issue.severity]) {
105+
issuesBySeverity[issue.severity].push(issue);
106+
} else {
107+
issuesBySeverity.warning.push(issue);
108+
}
109+
});
110+
}
111+
} catch (error) {
112+
console.log(`⚠️ Rule ${rule.id} failed: ${error.message}`);
113+
}
114+
});
115+
116+
// Display results
117+
this.displayResults(totalIssues, issuesBySeverity, issuesByRule, source);
118+
119+
// Save results
120+
const results = {
121+
timestamp: new Date().toISOString(),
122+
source: source,
123+
contentLength: content.length,
124+
totalIssues: totalIssues,
125+
issuesBySeverity: {
126+
errors: issuesBySeverity.error.length,
127+
warnings: issuesBySeverity.warning.length,
128+
suggestions: issuesBySeverity.suggestion.length
129+
},
130+
issues: issuesBySeverity
131+
};
132+
133+
fs.writeFileSync('content-validation-results.json', JSON.stringify(results, null, 2));
134+
console.log('💾 Detailed results saved to: content-validation-results.json');
135+
136+
} catch (error) {
137+
console.error('❌ Validation failed:', error.message);
138+
}
139+
}
140+
141+
displayResults(totalIssues, issuesBySeverity, issuesByRule, source) {
142+
console.log('📊 Validation Results');
143+
console.log('====================');
144+
console.log(`Total issues found: ${totalIssues}`);
145+
console.log(`🚨 Critical errors: ${issuesBySeverity.error.length}`);
146+
console.log(`⚠️ Warnings: ${issuesBySeverity.warning.length}`);
147+
console.log(`💡 Suggestions: ${issuesBySeverity.suggestion.length}`);
148+
console.log('');
149+
150+
if (totalIssues === 0) {
151+
console.log('🎉 Perfect! Your content follows all 12 Strapi rules!');
152+
console.log('');
153+
return;
154+
}
155+
156+
// Show critical errors first
157+
if (issuesBySeverity.error.length > 0) {
158+
console.log('🚨 CRITICAL ERRORS (must fix):');
159+
console.log('================================');
160+
issuesBySeverity.error.forEach(issue => {
161+
console.log(`📍 Line ${issue.line}: ${issue.message}`);
162+
if (issue.suggestion) {
163+
console.log(` 💡 ${issue.suggestion}`);
164+
}
165+
console.log(` 📚 Rule: ${issue.ruleId}`);
166+
console.log('');
167+
});
168+
}
169+
170+
// Show warnings
171+
if (issuesBySeverity.warning.length > 0) {
172+
console.log('⚠️ WARNINGS (should address):');
173+
console.log('===============================');
174+
issuesBySeverity.warning.forEach(issue => {
175+
console.log(`📍 Line ${issue.line}: ${issue.message}`);
176+
if (issue.suggestion) {
177+
console.log(` 💡 ${issue.suggestion}`);
178+
}
179+
console.log(` 📚 Rule: ${issue.ruleId}`);
180+
console.log('');
181+
});
182+
}
183+
184+
// Show suggestions
185+
if (issuesBySeverity.suggestion.length > 0) {
186+
console.log('💡 SUGGESTIONS (nice to have):');
187+
console.log('===============================');
188+
issuesBySeverity.suggestion.forEach(issue => {
189+
console.log(`📍 Line ${issue.line}: ${issue.message}`);
190+
if (issue.suggestion) {
191+
console.log(` 💡 ${issue.suggestion}`);
192+
}
193+
console.log(` 📚 Rule: ${issue.ruleId}`);
194+
console.log('');
195+
});
196+
}
197+
198+
// Summary by rule
199+
console.log('📋 Issues by Rule:');
200+
console.log('==================');
201+
Object.entries(issuesByRule).forEach(([ruleId, data]) => {
202+
console.log(`${data.rule.category === 'critical' ? '🚨' : '⚠️'} ${ruleId}: ${data.issues.length} issue(s)`);
203+
});
204+
console.log('');
205+
206+
// GitHub-style comment
207+
console.log('📝 GitHub Comment Preview:');
208+
console.log('===========================');
209+
const comment = this.generateGitHubComment(totalIssues, issuesBySeverity);
210+
console.log(comment);
211+
}
212+
213+
generateGitHubComment(totalIssues, issuesBySeverity) {
214+
let comment = `## 🎯 Strapi Documentation Style Review (LOCAL TEST)\n\n`;
215+
comment += `*Automated analysis using Strapi's 12 Rules of Technical Writing*\n\n`;
216+
217+
comment += '### 📊 Analysis Results\n';
218+
comment += `- **Total issues:** ${totalIssues}\n`;
219+
comment += `- **Critical errors:** ${issuesBySeverity.error.length} 🚨\n`;
220+
comment += `- **Warnings:** ${issuesBySeverity.warning.length} ⚠️\n`;
221+
comment += `- **Suggestions:** ${issuesBySeverity.suggestion.length} 💡\n\n`;
222+
223+
if (totalIssues === 0) {
224+
comment += '🎉 **Perfect!** Your content follows all 12 technical writing rules.\n\n';
225+
} else {
226+
comment += `⚠️ Found ${totalIssues} issues that should be addressed.\n\n`;
227+
228+
// Show top 3 issues
229+
const allIssues = [...issuesBySeverity.error, ...issuesBySeverity.warning, ...issuesBySeverity.suggestion];
230+
if (allIssues.length > 0) {
231+
comment += '**Top Issues:**\n';
232+
allIssues.slice(0, 3).forEach(issue => {
233+
comment += `- Line ${issue.line}: ${issue.message}\n`;
234+
});
235+
comment += '\n';
236+
}
237+
}
238+
239+
comment += '**📚 Resources:**\n';
240+
comment += '- [Strapi\'s 12 Rules of Technical Writing](https://strapi.notion.site/12-Rules-of-Technical-Writing-c75e080e6b19432287b3dd61c2c9fa04)\n';
241+
comment += '- [Documentation Style Guide](https://github.com/strapi/documentation/blob/main/STYLE_GUIDE.pdf)\n\n';
242+
comment += '*🧪 This is a local test simulation.*\n';
243+
244+
return comment;
245+
}
246+
}
247+
248+
// Command line interface
249+
async function main() {
250+
const args = process.argv.slice(2);
251+
const tester = new FlexibleContentTester();
252+
253+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
254+
console.log('🎯 Strapi Content Validation Tester');
255+
console.log('====================================');
256+
console.log('');
257+
console.log('Usage:');
258+
console.log(' node test-content.js --file <path> Test a specific file');
259+
console.log(' node test-content.js --text "<text>" Test provided text');
260+
console.log(' node test-content.js --stdin Test content from stdin');
261+
console.log('');
262+
console.log('Examples:');
263+
console.log(' node test-content.js --file docs/api/content-types.md');
264+
console.log(' node test-content.js --text "This is easy to understand"');
265+
console.log(' echo "Some content" | node test-content.js --stdin');
266+
console.log(' node test-content.js --stdin # then paste content manually');
267+
console.log('');
268+
return;
269+
}
270+
271+
if (args.includes('--file') || args.includes('-f')) {
272+
const fileIndex = args.findIndex(arg => arg === '--file' || arg === '-f');
273+
const filePath = args[fileIndex + 1];
274+
275+
if (!filePath) {
276+
console.error('❌ Please provide a file path after --file');
277+
return;
278+
}
279+
280+
await tester.testFile(filePath);
281+
282+
} else if (args.includes('--text') || args.includes('-t')) {
283+
const textIndex = args.findIndex(arg => arg === '--text' || arg === '-t');
284+
const text = args[textIndex + 1];
285+
286+
if (!text) {
287+
console.error('❌ Please provide text after --text');
288+
return;
289+
}
290+
291+
await tester.testText(text);
292+
293+
} else if (args.includes('--stdin') || args.includes('-s')) {
294+
await tester.testStdin();
295+
296+
} else {
297+
console.error('❌ Unknown option. Use --help for usage information.');
298+
}
299+
}
300+
301+
if (require.main === module) {
302+
main().catch(console.error);
303+
}
304+
305+
module.exports = FlexibleContentTester;

0 commit comments

Comments
 (0)