Skip to content

Commit

Permalink
lint fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
DavertMik committed Jul 1, 2023
1 parent cd69757 commit f28c0d3
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 150 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"env": {
"node": true
},
"parserOptions": {
"ecmaVersion": 2020
},
"rules": {
"func-names": 0,
"no-use-before-define": 0,
Expand Down
8 changes: 6 additions & 2 deletions examples/codecept.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ exports.config = {
output: './output',
helpers: {
Playwright: {
url: 'http://localhost',
url: 'http://github.com',
browser: 'chromium',
restart: 'context',
// restart: 'context',
// show: false,
// timeout: 5000,
windowSize: '1600x1200',
// video: true,
Expand Down Expand Up @@ -52,6 +53,9 @@ exports.config = {
autoDelay: {
enabled: false,
},
heal: {
enabled: true,
},
retryFailedStep: {
enabled: false,
},
Expand Down
6 changes: 6 additions & 0 deletions examples/github_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ Before(({ Smth }) => {
Smth.openGitHub();
});

Scenario('Incorrect search for Codeceptjs', ({ I }) => {
I.fillField('.search', 'CodeceptJS');
I.pressKey('Enter');
I.see('Supercharged End 2 End Testing');
});

Scenario('Visit Home Page @retry', async ({ I }) => {
// .retry({ retries: 3, minTimeout: 1000 })
I.retry(2).see('GitHub');
Expand Down
2 changes: 1 addition & 1 deletion jsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es2017",
"target": "es2019",
"module": "commonjs"
}
}
72 changes: 28 additions & 44 deletions lib/ai.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { removeNonInteractiveElements, minifyHtml, splitByChunks } = require('./h
const defaultConfig = {
model: 'gpt-3.5-turbo-16k',
temperature: 0.1,
}
};

const htmlConfig = {
maxLength: null,
Expand All @@ -16,40 +16,41 @@ const htmlConfig = {
interactiveElements: ['a', 'input', 'button', 'select', 'textarea', 'option'],
textElements: ['label', 'h1', 'h2'],
allowedAttrs: ['id', 'for', 'class', 'name', 'type', 'value', 'aria-labelledby', 'aria-label', 'label', 'placeholder', 'title', 'alt', 'src', 'role'],
allowedRoles: ['button', 'checkbox', 'search', 'textbox', 'tab'],
}
allowedRoles: ['button', 'checkbox', 'search', 'textbox', 'tab'],
};

class AiAssistant {

constructor() {
this.config = config.get('openai', defaultConfig);
this.htmlConfig = this.config.html || htmlConfig;
delete this.config.html;
this.html = null;
this.response = null;

this.isEnabled = !!process.env.OPENAI_API_KEY;

if (!this.isEnabled) return;

const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});

this.openai = new OpenAIApi(configuration);
}

setHtmlContext(html) {
let processedHTML = html;

if (this.htmlConfig.simplify) processedHTML = removeNonInteractiveElements(processedHTML, {
interactiveElements: this.htmlConfig.interactiveElements,
allowedAttrs: this.htmlConfig.allowedAttrs,
allowedRoles: this.htmlConfig.allowedRoles,
});
if (this.htmlConfig.simplify) {
processedHTML = removeNonInteractiveElements(processedHTML, {
interactiveElements: this.htmlConfig.interactiveElements,
allowedAttrs: this.htmlConfig.allowedAttrs,
allowedRoles: this.htmlConfig.allowedRoles,
});
}
if (this.htmlConfig.minify) processedHTML = minifyHtml(processedHTML);
if (this.htmlConfig.maxLength) processedHTML = splitByChunks(processedHTML, this.htmlConfig.maxLength)[0];

debug(processedHTML);

this.html = processedHTML;
Expand All @@ -66,7 +67,7 @@ class AiAssistant {
async createCompletion(messages) {
if (!this.openai) return;

debug(messages)
debug(messages);

if (this.mockedResponse) return this.mockedResponse;

Expand Down Expand Up @@ -119,18 +120,20 @@ class AiAssistant {
const snippets = [];

const messages = [
{ role: 'user',
{
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 valid CodeceptJS code to accomplish it
Use only locators from this HTML: \n\n${this.html}` },
{ role: 'user', content: `Propose only CodeceptJS steps code. Do not include Scenario or Feature into response` },
Use only locators from this HTML: \n\n${this.html}`,
},
{ role: 'user', content: 'Propose only CodeceptJS steps code. Do not include Scenario or Feature into response' },

// old prompt
// { role: 'user', content: 'I want to click button Submit using CodeceptJS on this HTML page: <html><body><button>Submit</button></body></html>' },
// { role: 'user', content: 'I want to click button Submit using CodeceptJS on this HTML page: <html><body><button>Submit</button></body></html>' },
// { role: 'assistant', content: '```js\nI.click("Submit");\n```' },
// { role: 'user', content: 'I want to click button Submit using CodeceptJS on this HTML page: <html><body><button>Login</button></body></html>' },
// { role: 'assistant', content: 'No suggestions' },
// { role: 'user', content: 'I want to click button Submit using CodeceptJS on this HTML page: <html><body><button>Login</button></body></html>' },
// { role: 'assistant', content: 'No suggestions' },
// { role: 'user', content: `Now I want to ${input} on this HTML page using CodeceptJS code` },
// { role: 'user', content: `Provide me with CodeceptJS code to achieve this on THIS page.` },
];
Expand All @@ -144,24 +147,6 @@ class AiAssistant {
}
}

class DummyAi extends AiAssistant {

constructor() {
super();
this.isEnabled = true;
}

setResponse(response) {
this.response = response;
return this;
}

async createCompletion(messages) {
debug(messages);
return this.response || 'Dummy AI response';
}
}

function parseCodeBlocks(response) {
// Regular expression pattern to match code snippets
const codeSnippetPattern = /```(?:javascript|js|typescript|ts)?\n([\s\S]+?)\n```/g;
Expand All @@ -178,15 +163,14 @@ function parseCodeBlocks(response) {
// Remove "Scenario", "Feature", and "require()" lines
const modifiedSnippets = codeSnippets.map(snippet => {
const lines = snippet.split('\n').map(line => line.trim());

const filteredLines = lines.filter(line => !line.includes('I.amOnPage') && !line.startsWith('Scenario') && !line.startsWith('Feature') && !line.includes('= require('));

return filteredLines.join('\n');
// remove snippets that move from current url
}); // .filter(snippet => !line.includes('I.amOnPage'));
}); // .filter(snippet => !line.includes('I.amOnPage'));

return modifiedSnippets.filter(snippet => !!snippet);
return modifiedSnippets.filter(snippet => !!snippet);
}

module.exports = AiAssistant;
AiAssistant.DummyAi = DummyAi;
5 changes: 2 additions & 3 deletions lib/command/interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ module.exports = async function (path, options) {
});
event.emit(event.test.before, {
title: '',
artifacts: {}
artifacts: {},
});


const enabledHelpers = Container.helpers();
for (const helperName of Object.keys(enabledHelpers)) {
for (const helperName of Object.keys(enabledHelpers)) {
if (webHelpers.includes(helperName)) {
const I = enabledHelpers[helperName];
recorder.add(() => I.amOnPage('/'));
Expand Down
53 changes: 23 additions & 30 deletions lib/helper/OpenAI.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const Helper = require('@codeceptjs/helper');
const AiAssistant = require('../ai');
const standardActingHelpers = require('../plugin/standardActingHelpers');
const Container = require('../container');
const { splitByChunks } = require('../html');
const { splitByChunks, minifyHtml } = require('../html');

/**
* OpenAI Helper for CodeceptJS.
Expand All @@ -11,20 +11,19 @@ const { splitByChunks } = require('../html');
* This helper should be enabled with any web helpers like Playwright or Puppeteer or WebDrvier to ensure the HTML context is available.
*
* ## Configuration
*
*
* This helper should be configured in codecept.json or codecept.conf.js
*
*
* * `chunkSize`: (optional, default: 80000) - The maximum number of characters to send to the OpenAI API at once. We split HTML fragments by 8000 chars to not exceed token limit. Increase this value if you use GPT-4.
*/
class OpenAI extends Helper {

constructor(config) {
super(config);
this.aiAssistant = new AiAssistant();

this.options = {
chunkSize: 80000,
};
};
this.options = { ...this.options, ...config };

const helpers = Container.helpers();
Expand All @@ -37,10 +36,9 @@ class OpenAI extends Helper {
}
}


/**
/**
* Asks the OpenAI GPT language model a question based on the provided prompt within the context of the current page's HTML.
*
*
* ```js
* I.askGptOnPage('what does this page do?');
* ```
Expand All @@ -51,21 +49,20 @@ class OpenAI extends Helper {
*/
async askGptOnPage(prompt) {
const html = await this.helper.grabSource();

const htmlChunks = splitByChunks(html, this.options.chunkSize);

if (htmlChunks.length > 1) this.debug(`Splitting HTML into ${}`)

const responses = [];
const htmlChunks = splitByChunks(html, this.options.chunkSize);

for (const chunk of htmlChunks) {
if (htmlChunks.length > 1) this.debug(`Splitting HTML into ${htmlChunks.length} chunks`);

const responses = [];

for (const chunk of htmlChunks) {
const messages = [
{ role: 'user', content: prompt },
{ role: 'user', content: `Within this HTML: ${chunk}` },
]
{ role: 'user', content: `Within this HTML: ${minifyHtml(chunk)}` },
];

if (htmlChunks.length > 1) messages.push({ role: 'user', content: `If action is not possible on this page, do not propose anything, I will send another HTML fragment` });
if (htmlChunks.length > 1) messages.push({ role: 'user', content: 'If action is not possible on this page, do not propose anything, I will send another HTML fragment' });

const response = await this.aiAssistant.createCompletion(messages);

Expand All @@ -83,20 +80,19 @@ class OpenAI extends Helper {
* ```js
* I.askGptOnPageFragment('describe features of this screen', '.screen');
* ```
*
*
* @async
* @param {string} prompt - The question or prompt to ask the GPT-3.5 model.
* @param {string} locator - The locator or selector used to identify the HTML fragment on the page.
* @returns {Promise<string>} - A Promise that resolves to the generated response from the GPT-3.5 model.
*/
askGptOnPageFragment(prompt, locator) {

async askGptOnPageFragment(prompt, locator) {
const html = await this.helper.grabHTMLFrom(locator);

const messages = [
{ role: 'user', content: prompt },
{ role: 'user', content: `Within this HTML: ${html}` },
]
{ role: 'user', content: `Within this HTML: ${minifyHtml(html)}` },
];

const response = await this.aiAssistant.createCompletion(messages);

Expand All @@ -107,14 +103,13 @@ class OpenAI extends Helper {

/**
* Send a general request to ChatGPT and return response.
* @param {string} prompt
* @returns
* @param {string} prompt
* @returns
*/
askGptGeneralPrompt(prompt) {

async askGptGeneralPrompt(prompt) {
const messages = [
{ role: 'user', content: prompt },
]
];

const completion = await this.aiAssistant.createCompletion(messages);

Expand All @@ -124,6 +119,4 @@ class OpenAI extends Helper {

return response;
}


}
}
Loading

0 comments on commit f28c0d3

Please sign in to comment.