fintech-automation-test
Version:
Autonomous Test Automation
194 lines (178 loc) • 6.49 kB
JavaScript
const axios = require('axios');
const fs = require('fs');
const config = require('../../../config');
const log4js = require('log4js');
const FormData = require('form-data');
const { initializeOpenAI } = require('./openaiUtils');
const aiAssistant = require('codeceptjs/lib/ai');
const ora = require('ora-classic');
const output = require('codeceptjs/lib/output');
const { error } = require('codeceptjs/lib/output');
const recorder = require('codeceptjs/lib/recorder');
const I2 = require('codeceptjs/lib/actor');
const colors = require('chalk');
const { event } = require('codeceptjs');
const debug = require('debug')('codeceptjs:pause');
const defaultPrompts = {
writeStep: (html, input) => [
{
role: 'user',
content: `I am test engineer writing test in CodeceptJS
I have opened web page and I want to use CodeceptJS to ${input} on this page
Provide me only one valid CodeceptJS command to accomplish it
Use only locators from this HTML: \n\n${html}`,
},
],
};
class AiUtils {
async getAICommand(I, cmdText, filePath, step) {
console.log('cmdText: ' + cmdText);
let i = 0;
let matches = [];
let cmd;
try {
await this.addStepToFile('//Step-' + step + ':' + cmdText + '\n', filePath);
do {
if (cmdText.includes('pop up')) I.wait(12);
I.wait(5);
const res = await I.grabSource();
const html = res;
await aiAssistant.setHtmlContext(html);
const spinner = ora('Processing AI request...').start();
// await this.addStepToFile('I.wait(4)' + '\n', filePath)
cmd = await this.writeSteps(cmdText);
spinner.stop(); // Stop the spinner after the AI request is complete
console.log('Response From AI: ' + cmd);
//let removedText = await removeUnwantedCode(cmd);
matches = await this.extractCodeceptJSCommands(cmd);
i++;
} while (matches.length == 0 && i < 3);
//await addStepToFile(cmd+ '\n', filePath);
console.log('commands: ' + matches);
console.log('matches.length: ' + matches.length);
if (matches.length > 0) {
if (cmdText.includes('fill')) {
for (const step of matches) {
try {
console.log(`Step ---->${step}`);
I.wait(4);
await eval(`${step}`);
await this.addStepToFile(step + '\n', filePath);
} catch (evalErr) {
await this.addStepToFile(
step +
'//Suggestion: Replace with the correct locator.\n',
filePath
);
await recorder.catchWithoutStop((evalErr) =>
output.print(colors.yellow("Encountered an error while interacting with the page. A suggestion will be generated for this step."+evalErr))
);
// Continue execution even if this step fails
}
}
} else {
try {
I.wait(4);
await eval(`${matches[0]}`);
await this.addStepToFile(matches[0] + '\n', filePath);
await this.addStepToFile('I.wait(4)' + '\n', filePath);
} catch (evalErr) {
await this.addStepToFile(
matches[0] +
'//Suggestion: Replace with the correct locator.\n',
filePath
);
await this.addStepToFile('I.wait(4)' + '\n', filePath);
await recorder.catchWithoutStop((evalErr) =>
output.print(
colors.bgBlack.bold.white("Encountered an error while interacting with the page. A suggestion will be generated for this step: " + evalErr))
);
// Continue execution even if this step fails
}
}
} else {
await this.addStepToFile(cmd + '\n', filePath);
await recorder.catchWithoutStop((evalErr) =>
output.print(
colors.bgBlack.bold.white("Encountered an error while interacting with the page. A suggestion will be generated for this step: " + evalErr))
);
// Continue execution even if this step fails
}
} catch (err) {
console.log('error==>' + err);
await recorder.catchWithoutStop((evalErr) =>
output.print(
colors.bgBlack.bold.white("Encountered an error while interacting with the page. A suggestion will be generated for this step: " + evalErr))
);
// Continue execution even if this step fails
}
}
async removeUnwantedCode(text) {
// Remove lines that contain "Navigate", "Assuming", `});`, and `const { I } = inject();`
return text
.split('\n') // Split the text into an array of lines
.filter(
(line) => !line.includes('Navigate') && !line.includes('Assuming')
) // Filter out lines containing "Navigate" and "Assuming"
.join('\n') // Join the lines back into a single string
.replace(/\}\);/g, '') // Remove closing `});`
.replace(/const\s\{\sI\s\}\s=\sinject\(\);\s*/g, ''); // Remove `const { I } = inject();`
}
async extractCodeceptJSCommands(text) {
const commandRegex = /(I\.\w+)\(([\s\S]+?)\);/g;
const matches = [];
let match;
// Find all matching CodeceptJS commands using regex
while ((match = commandRegex.exec(text)) !== null) {
const actionWithI = match[1];
// Skip commands if they include 'wait' or 'see' or if their size is 3 or less
if (
actionWithI.toLowerCase().includes('wait') ||
actionWithI.toLowerCase().includes('see') ||
actionWithI.length <= 3 // Skip if length is 3 or less
) {
continue;
}
const args = match[2].trim();
matches.push(`${actionWithI}(${args})`);
}
return matches;
}
async extractCodeceptJSCommands2(text) {
// const commandRegex = /(I\.\w+)\(([\s\S]+?)\);/g;
// const matches = [];
// let match;
// while ((match = commandRegex.exec(text)) !== null) {
// const actionWithI = match[1];
// if (
// actionWithI.toLowerCase().includes('wait') ||
// actionWithI.toLowerCase().includes('see')
// ) {
// continue;
// }
// const args = match[2].trim();
// matches.push(`${actionWithI}(${args})`);
// }
const linesArray = text
.split('\n')
.map((line) => line.trim())
.filter((line) => line.length > 0);
return linesArray;
}
async addStepToFile(aiCommand, filePath) {
fs.appendFileSync(filePath, aiCommand + '\n', 'utf-8');
}
async writeSteps(input) {
if (!aiAssistant.isEnabled) return;
if (!aiAssistant.minifiedHtml) throw new Error('No HTML context provided');
const snippets = [];
const response = await aiAssistant.createCompletion(
defaultPrompts.writeStep(aiAssistant.minifiedHtml, input)
);
if (!response) return;
snippets.push(...aiAssistant.config.response(response));
debug(snippets[0]);
return snippets[0];
}
}
module.exports = new AiUtils();