Files
festipod/scripts/extract-step-definitions.ts
T
Sylvain Duchesne 9cc916e8bc Fix TypeScript strict null check errors
Add optional chaining and null checks in build scripts to handle
potentially undefined array elements. Add style prop to Card, Badge,
and Placeholder components, and onClick prop to Text component to
support inline styling in screen components.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 21:16:21 +01:00

123 lines
3.5 KiB
TypeScript

// Extract step definitions from feature files and generate a data file with source code
import * as fs from 'fs';
import * as path from 'path';
interface StepDefinition {
pattern: string;
keyword: 'Given' | 'When' | 'Then';
file: string;
sourceCode: string;
lineNumber: number;
}
const stepFiles = [
'features/step_definitions/navigation.steps.ts',
'features/step_definitions/form.steps.ts',
'features/step_definitions/screen.steps.ts',
];
function extractStepDefinitions(): StepDefinition[] {
const definitions: StepDefinition[] = [];
for (const filePath of stepFiles) {
const fullPath = path.join(process.cwd(), filePath);
if (!fs.existsSync(fullPath)) continue;
const content = fs.readFileSync(fullPath, 'utf-8');
const lines = content.split('\n');
const fileName = path.basename(filePath);
// Find step definitions line by line
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Match Given/When/Then at the start of a line
// Handle escaped quotes in patterns (e.g., 'l\'écran contient')
const match = line?.match(/^(Given|When|Then)\s*\(\s*(['"`])((?:[^\\]|\\.)*?)\2/);
if (match && match[3]) {
const keyword = match[1] as 'Given' | 'When' | 'Then';
// Unescape the pattern (remove backslashes before quotes)
const pattern = match[3].replace(/\\(['"`])/g, '$1');
// Extract the full function body
const sourceCode = extractFunctionBody(lines, i);
definitions.push({
pattern,
keyword,
file: fileName,
sourceCode,
lineNumber: i + 1,
});
}
}
}
return definitions;
}
function extractFunctionBody(lines: string[], startLine: number): string {
// Look for the closing }); which marks the end of a step definition
for (let i = startLine; i < lines.length; i++) {
const line = lines[i]?.trim();
if (line === '});' || line?.endsWith('});')) {
const extracted = lines.slice(startLine, i + 1);
return extracted.join('\n');
}
}
// Fallback: return just the start line if we couldn't find the end
return lines[startLine] || '';
}
async function generateStepDefinitionsFile() {
const definitions = extractStepDefinitions();
const findFunctionCode = `export function findStepDefinition(stepText: string): StepDefinitionInfo | null {
for (const def of stepDefinitions) {
// Convert Cucumber expression to regex
// {string} -> "[^"]+"
// {int} -> \\\\d+
const regexPattern = def.pattern
.replace(/\\{string\\}/g, '"[^"]+"')
.replace(/\\{int\\}/g, '\\\\d+');
try {
const regex = new RegExp(regexPattern);
if (regex.test(stepText)) {
return def;
}
} catch {
// If pattern fails, try simple includes
const simplified = def.pattern.replace(/\\{string\\}/g, '').replace(/\\{int\\}/g, '').trim();
if (stepText.includes(simplified)) {
return def;
}
}
}
return null;
}`;
const output = `// Auto-generated by scripts/extract-step-definitions.ts
// Do not edit manually - run "bun run steps:extract" to regenerate
export interface StepDefinitionInfo {
pattern: string;
keyword: 'Given' | 'When' | 'Then';
file: string;
sourceCode: string;
lineNumber: number;
}
export const stepDefinitions: StepDefinitionInfo[] = ${JSON.stringify(definitions, null, 2)};
${findFunctionCode}
`;
await Bun.write('src/data/stepDefinitions.ts', output);
console.log(`Generated ${definitions.length} step definitions`);
}
generateStepDefinitionsFile().catch(console.error);