Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
@slack/enzyme-to-rtl-codemod
Advanced tools
AST codemod for Enzyme to RTL with AI integration capabilities
This package automates the conversion of Jest tests from Enzyme to React Testing Library (RTL). It is designed to be used with your own large language model (LLM) and your implementation for programmatically making API requests to retrieve LLM responses based on the prompts generated by this tool.
In response to numerous requests from external developers, we are open-sourcing a version of our Slack-built tool for converting Enzyme tests to React Testing Library (RTL). While this tool is not a complete solution for all use cases, it serves as a starting point for automating the migration process. With over 1.5 million Enzyme downloads from npm (as of September 2024), our goal is to ease this transition, save time, and demonstrate a practical application of LLM integration in developer workflows.
We hope this tool proves useful. We encourage contributions to this repository or forking it to make necessary adjustments. We will provide limited support for reviewing critical bug fixes.
npm install @slack/enzyme-to-rtl-codemod
or
yarn add @slack/enzyme-to-rtl-codemod
There are three ways to use this package:
convertTestFiles({...})
convertTestFiles()
function:// Import convertTestFiles and LLMCallFunction type
import {
convertTestFiles,
type LLMCallFunction,
} from '@slack/enzyme-to-rtl-codemod';
// Example implementation of the LLM call function
const callLLMFunctionExample: LLMCallFunction = async (
prompt: string,
): Promise<string> => {
// Step 1: Configure LLM parameters
const config = {
// Add your LLM configuration parameters here
// Lowering the temperature (e.g., to 0.2) may yield more deterministic results
};
// Step 2: Call the LLM with the provided prompt
const LLLresponse = await callLLMapi(config, prompt);
// Step 3: Return the result
return LLLresponse;
};
// Implement convertTestFiles function call with your arguments
const convertFiles = async (filePaths: string[]) => {
const results = await convertTestFiles({
filePaths: filePaths,
jestBinaryPath: 'npx jest', // this command should be able to run one jest test file, e.g. `npx jest <filePath>`
outputResultsPath: 'ai-conversion-testing/temp',
testId: '<your_custom_test_id_attribute', // defaults to RTL `data-testid` attribute
llmCallFunction: callLLMFunctionExample,
enableFeedbackStep: true,
});
console.log('Results:', results);
};
const enzymeFilePaths = [
'path/to/your/enzymeFile1.jest.tsx',
'path/to/your/enzymeFile2.jest.tsx',
];
// Run the function and check logs and outputResultsPath for results
convertFiles(enzymeFilePaths);
This approach gives you more control over the flow and allows you to inspect the output of each method. You may also want to extract only the AST-converted code. Important: The methods must be called in the correct order, as the flow depends on it.
// Import the required methods
import {
initializeConfig,
convertWithAST,
getReactCompDom,
generateInitialPrompt,
extractCodeContentToFile,
runTestAndAnalyze,
generateFeedbackPrompt,
} from '@slack/enzyme-to-rtl-codemod';
// Import the LLM api call method helper
import { callLLM } from './llm-helper';
const convertTestFile = async (filePath: string): Promise<void> => {
// Initialize config
const config = initializeConfig({
filePath,
jestBinaryPath: 'npx jest', // this command should be able to run one jest test file, e.g. `npx jest <filePath>`
outputResultsPath: 'ai-conversion-testing/temp',
testId: '<your_custom_test_id_attribute', // defaults to RTL `data-testid` attribute
});
// Perform AST conversion
const astConvertedCode = convertWithAST({
filePath,
testId: config.testId,
astTransformedFilePath: config.astTranformedFilePath,
});
// Get React Component DOM tree for each test case
const reactCompDom = await getReactCompDom({
filePath,
enzymeImportsPresent: config.enzymeImportsPresent,
filePathWithEnzymeAdapter: config.filePathWithEnzymeAdapter,
collectedDomTreeFilePath: config.collectedDomTreeFilePath,
enzymeMountAdapterFilePath: config.enzymeMountAdapterFilePath,
jestBinaryPath: config.jestBinaryPath,
reactVersion: config.reactVersion,
});
// Generate the initial LLM prompt
const initialPrompt = generateInitialPrompt({
filePath,
getByTestIdAttribute: config.testId,
astCodemodOutput: astConvertedCode,
renderedCompCode: reactCompDom,
originalTestCaseNum: config.originalTestCaseNum,
});
/**
* Call LLM with the generated prompt
* 1. This would be specific for your LLM
* 2. We only provide tooling for context gathering and prompt generation
* 3. The prompt string should be LLM agnostic
*/
// Create a prompt, make a request, get a response
const LLMresponse = await callLLM(initialPrompt);
// Extract the generated code
const convertedFilePath = extractCodeContentToFile({
LLMresponse,
rtlConvertedFilePath: config.rtlConvertedFilePathAttmp1,
});
// Run the converted test file and analyze the results
const attempt1Result = await runTestAndAnalyze({
filePath: convertedFilePath,
jestBinaryPath: config.jestBinaryPath,
jestRunLogsPath: config.jestRunLogsFilePathAttmp1,
rtlConvertedFilePath: config.rtlConvertedFilePathAttmp1,
outputResultsPath: config.outputResultsPath,
originalTestCaseNum: config.originalTestCaseNum,
summaryFile: config.jsonSummaryPath,
attempt: 'attempt1',
});
// Store results
let finalResult = attempt1Result;
// Step to call LLM if attempt 1 failed
// This is optional, but can add 5-20% better results
if (!attempt1Result.attempt1.testPass) {
// Create feedback command
const feedbackPrompt = generateFeedbackPrompt({
rtlConvertedFilePathAttmpt1: config.rtlConvertedFilePathAttmp1,
getByTestIdAttribute: config.testId,
jestRunLogsFilePathAttmp1: config.jestRunLogsFilePathAttmp1,
renderedCompCode: reactCompDom,
originalTestCaseNum: config.originalTestCaseNum,
});
// Call the API with a custom LLM method
const LLMresponseAttmp2 = await callLLM(feedbackPrompt);
// Extract generated code
const convertedFeedbackFilePath = extractCodeContentToFile({
LLMresponse: LLMresponseAttmp2,
rtlConvertedFilePath: config.rtlConvertedFilePathAttmp2,
});
// Run the file and analyze the failures
const attempt2Result = await runTestAndAnalyze({
filePath: convertedFeedbackFilePath,
jestBinaryPath: config.jestBinaryPath,
jestRunLogsPath: config.jestRunLogsFilePathAttmp2,
rtlConvertedFilePath: config.rtlConvertedFilePathAttmp2,
outputResultsPath: config.outputResultsPath,
originalTestCaseNum: config.originalTestCaseNum,
summaryFile: config.jsonSummaryPath,
attempt: 'attempt2',
});
// Update finalResult to include attempt2Result
finalResult = attempt2Result;
}
// Output final result
console.log('final result:', finalResult);
};
// Run the function and see logs and files in `outputResultsPath`
convertTestFile('<testPath1>');
Results will be written to the outputResultsPath/<timeStampFolder>/<filePath>/*
folder.
Example:
└── 2024-09-05_16-15-41
├── <file-path>
| ├── ast-transformed-<file_title>.test.tsx - AST converted/annotated file
| ├── attmp-1-jest-run-logs-<file_title>.md - Jest run logs for RTL file attempt 1
| ├── attmp-1-rtl-converted-<file_title>.test.tsx - Converted Enzyme to RTL file attempt 1
| ├── attmp-2-jest-run-logs-<file_title>.md - Jest run logs for RTL file attempt 2
| ├── attmp-2-rtl-converted-<file_title>.test.tsx - Converted Enzyme to RTL file attempt 2
| ├── dom-tree-<file_title>.csv - Collected DOM for each test case in Enzyme file
| ├── enzyme-mount-adapter.js - Enzyme rendering methods with DOM logs collection logic
| └── enzyme-mount-overwritten-<file_title>.test.tsx - Enzyme file with new methods that emit DOM
mount
and shallow
imported directly from the Enzyme package. If your project uses helper methods to wrap Enzyme’s mount or shallow, this package may not work as expected.import { mount } from 'enzyme';
info
verbose
in convertFiles
or initializeConfig
This package exports the following:
convertTestFiles
- run the entire conversion flow in one function. Easy and fast way to start convertingLLMCallFunction
- llm call function typeinitializeConfig
- Initialize configuration settings required for the conversion process. This method prepares paths and settings, such as Jest binary, output paths, and test identifiers.convertWithAST
- Run jscodeshift and make AST conversions/annotations.getReactCompDom
- Get React component DOM for test cases.generateInitialPrompt
- Generate a prompt for an LLM to assist in converting Enzyme test cases to RTL.generateFeedbackPrompt
- Generate a feedback prompt for an LLM to assist in fixing React unit tests using RTL.extractCodeContentToFile
- Extract code content from an LLM response and write it to a file.runTestAndAnalyze
- Run an RTL test file with Jest and analyze the results.FAQs
AST codemod for Enzyme to RTL with AI integration capabilities
The npm package @slack/enzyme-to-rtl-codemod receives a total of 2 weekly downloads. As such, @slack/enzyme-to-rtl-codemod popularity was classified as not popular.
We found that @slack/enzyme-to-rtl-codemod demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.