add-test-ids
Version:
Add test ids to your react components
241 lines (220 loc) • 5.62 kB
JavaScript
const fs = require("fs");
const path = require("path");
const readline = require("readline");
// Array of MUI v4 elements
const muiElements = [
"AppBar",
"Avatar",
"Backdrop",
"Badge",
"BottomNavigation",
"BottomNavigationAction",
"Box",
"Button",
"ButtonBase",
"ButtonGroup",
"Card",
"CardActionArea",
"CardActions",
"CardContent",
"CardHeader",
"CardMedia",
"Checkbox",
"Chip",
"CircularProgress",
"ClickAwayListener",
"Collapse",
"Container",
"CssBaseline",
"Dialog",
"DialogActions",
"DialogContent",
"DialogContentText",
"DialogTitle",
"Divider",
"Drawer",
"ExpansionPanel",
"ExpansionPanelActions",
"ExpansionPanelDetails",
"ExpansionPanelSummary",
"Fab",
"Fade",
"FilledInput",
"FormControl",
"FormControlLabel",
"FormGroup",
"FormHelperText",
"FormLabel",
"Grid",
"GridList",
"GridListTile",
"GridListTileBar",
"Hidden",
"Icon",
"IconButton",
"Input",
"InputLabel",
"LinearProgress",
"Link",
"List",
"ListItem",
"ListItemAvatar",
"ListItemIcon",
"ListItemSecondaryAction",
"ListItemText",
"ListSubheader",
"Menu",
"MenuItem",
"MenuList",
"MobileStepper",
"Modal",
"NativeSelect",
"OutlinedInput",
"Pagination",
"PaginationItem",
"Paper",
"Popover",
"Popper",
"Portal",
"Radio",
"RadioGroup",
"RootRef",
"Select",
"Slide",
"Snackbar",
"SnackbarContent",
"Step",
"StepButton",
"StepConnector",
"StepContent",
"StepIcon",
"StepLabel",
"Stepper",
"SvgIcon",
"SwipeableDrawer",
"Switch",
"Tab",
"Table",
"TableBody",
"TableCell",
"TableContainer",
"TableFooter",
"TableHead",
"TablePagination",
"TableRow",
"TableSortLabel",
"Tabs",
"TextField",
"TextareaAutosize",
"Toolbar",
"Tooltip",
"Typography",
];
function hashString(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = (hash << 5) - hash + str.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return Math.abs(hash).toString(36).substring(0, 5);
}
// Function to add data-testId attribute
function addTestIDs(sourceCode, fileName) {
let updatedCode = sourceCode;
// Iterate over each MUI element and add data-testid attribute
muiElements.forEach((element, index) => {
// Create a regex pattern to match the element & exclude element that have existing data-testId attributes
const regex = new RegExp(
`<${element}(?![^>]*data-testId)(?![^>]*"[^"]*>")(\\s|>)`,
"gs"
);
let lineNumber = 0;
let columnNumber = 0;
updatedCode = updatedCode.replace(regex, (match, p1, offset) => {
lineNumber = sourceCode.substring(0, offset).split("\n").length;
columnNumber = offset - sourceCode.lastIndexOf("\n", offset);
let hashedLineNumber = hashString(lineNumber.toString()).substring(0, 3);
let hashedColumnNumber = hashString(columnNumber.toString()).substring(
0,
3
);
return `<${element} data-testId="${hashedLineNumber}-${hashedColumnNumber}"${p1}`;
});
});
return updatedCode;
}
// Function to process a single file
function processFile(filePath) {
// Read the source code file
fs.readFile(filePath, "utf8", (err, data) => {
if (err) {
console.error(err);
return;
}
// Update the source code with data-testid attributes
const updatedCode = addTestIDs(data, path.basename(filePath));
// Write the updated code back to the file
fs.writeFile(filePath, updatedCode, "utf8", (err) => {
if (err) {
console.error(err);
return;
}
console.log(`Test IDs added successfully to ${filePath}`);
});
});
}
// Function to process all files in a directory (including nested directories)
function processDirectory(dirPath, fileExtensions) {
// Read all files in the directory
fs.readdir(dirPath, (err, files) => {
if (err) {
console.error(err);
return;
}
// Process each file
files.forEach((file) => {
const filePath = path.join(dirPath, file);
// Check if the file is a directory
fs.stat(filePath, (err, stats) => {
if (err) {
console.error(err);
return;
}
if (stats.isDirectory()) {
// Recursively process the directory
processDirectory(filePath, fileExtensions);
} else if (fileExtensions.includes(path.extname(filePath))) {
// Process only .tsx files
processFile(filePath);
}
});
});
});
}
// // Start processing the src directory
// const srcPath = 'src'; // Adjust the path as needed
// processDirectory(srcPath);
function main() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question("Please enter a directory path: ", (dirPath) => {
rl.question(
"Please enter file extensions, separated by commas: ",
(fileExtensions) => {
fileExtensions = fileExtensions.split(",").map((ext) => ext.trim());
// Start processing the provided directory
processDirectory(dirPath, fileExtensions);
rl.close();
}
);
});
}
// Get command-line arguments
const args = process.argv.slice(2);
const dirPath = args[0];
const fileExtensions = args.slice(1);
// Run the main function
main(dirPath, fileExtensions);