add e2e test for CI

This commit is contained in:
misrasaurabh1 2026-01-27 20:54:01 -08:00
parent 7b7cc99f3a
commit 54ef71090e
51 changed files with 2813 additions and 9 deletions

View file

@ -30,6 +30,29 @@ describe('bubbleSort', () => {
bubbleSort(original);
expect(original).toEqual([3, 1, 2]);
});
test('sorts a larger reverse sorted array for performance', () => {
const input = [];
for (let i = 500; i >= 0; i--) {
input.push(i);
}
const result = bubbleSort(input);
expect(result[0]).toBe(0);
expect(result[result.length - 1]).toBe(500);
});
test('sorts a larger random array for performance', () => {
const input = [
42, 17, 93, 8, 67, 31, 55, 22, 89, 4,
76, 12, 39, 58, 95, 26, 71, 48, 83, 19,
64, 3, 88, 37, 52, 11, 79, 46, 91, 28,
63, 7, 84, 33, 57, 14, 72, 41, 96, 24,
69, 6, 81, 36, 54, 16, 77, 44, 90, 29
];
const result = bubbleSort(input);
expect(result[0]).toBe(3);
expect(result[result.length - 1]).toBe(96);
});
});
describe('bubbleSortDescending', () => {

View file

@ -26,6 +26,20 @@ describe('reverseString', () => {
test('handles spaces', () => {
expect(reverseString('hello world')).toBe('dlrow olleh');
});
test('reverses a longer string for performance', () => {
const input = 'abcdefghijklmnopqrstuvwxyz'.repeat(20);
const result = reverseString(input);
expect(result.length).toBe(input.length);
expect(result[0]).toBe('z');
expect(result[result.length - 1]).toBe('a');
});
test('reverses a medium string', () => {
const input = 'The quick brown fox jumps over the lazy dog';
const expected = 'god yzal eht revo spmuj xof nworb kciuq ehT';
expect(reverseString(input)).toBe(expected);
});
});
describe('isPalindrome', () => {

View file

@ -0,0 +1,64 @@
/**
* Async utility functions - ES Module version.
* Contains intentionally inefficient implementations for optimization testing.
*/
/**
* Simulate a delay (for testing purposes).
* @param {number} ms - Milliseconds to delay
* @returns {Promise<void>}
*/
export function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Process items sequentially when they could be parallel.
* Intentionally inefficient - processes items one at a time.
* @param {any[]} items - Items to process
* @param {function} processor - Async function to process each item
* @returns {Promise<any[]>} Processed results
*/
export async function processItemsSequential(items, processor) {
const results = [];
for (let i = 0; i < items.length; i++) {
const result = await processor(items[i]);
results.push(result);
}
return results;
}
/**
* Map over items asynchronously with a concurrency limit.
* Intentionally simple/inefficient implementation - ignores concurrency.
* @param {any[]} items - Items to process
* @param {function} mapper - Async mapper function
* @param {number} concurrency - Max concurrent operations (currently ignored)
* @returns {Promise<any[]>} Mapped results
*/
export async function asyncMap(items, mapper, concurrency = 1) {
// Inefficient: ignores concurrency, processes sequentially
const results = [];
for (const item of items) {
results.push(await mapper(item));
}
return results;
}
/**
* Filter items asynchronously.
* Inefficient implementation that processes items one by one.
* @param {any[]} items - Items to filter
* @param {function} predicate - Async predicate function
* @returns {Promise<any[]>} Filtered items
*/
export async function asyncFilter(items, predicate) {
const results = [];
for (const item of items) {
const shouldInclude = await predicate(item);
if (shouldInclude) {
results.push(item);
}
}
return results;
}

View file

@ -8,10 +8,38 @@
"name": "code-to-optimize-js-esm",
"version": "1.0.0",
"devDependencies": {
"@eslint/js": "^9.39.2",
"codeflash": "file:../packages/codeflash",
"eslint": "^9.39.2",
"globals": "^17.1.0",
"jest": "^29.7.0",
"jest-junit": "^16.0.0"
}
},
"../packages/codeflash": {
"version": "0.1.0",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"better-sqlite3": "^9.0.0"
},
"bin": {
"codeflash": "bin/codeflash.js",
"codeflash-setup": "bin/codeflash-setup.js"
},
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"jest": ">=27.0.0"
},
"peerDependenciesMeta": {
"jest": {
"optional": true
}
}
},
"node_modules/@babel/code-frame": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
@ -508,6 +536,235 @@
"dev": true,
"license": "MIT"
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
"node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint-community/regexpp": {
"version": "4.12.2",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
"integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/config-array": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.17.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
"integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
"espree": "^10.0.1",
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.1",
"minimatch": "^3.1.2",
"strip-json-comments": "^3.1.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/eslintrc/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/@eslint/js": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://eslint.org/donate"
}
},
"node_modules/@eslint/object-schema": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
"integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.17.0",
"levn": "^0.4.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/core": "^0.19.1",
"@humanwhocodes/retry": "^0.4.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.22"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@humanwhocodes/retry": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@ -949,6 +1206,13 @@
"@babel/types": "^7.28.2"
}
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/graceful-fs": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@ -986,6 +1250,13 @@
"@types/istanbul-lib-report": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.0.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz",
@ -1020,6 +1291,46 @@
"dev": true,
"license": "MIT"
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-jsx": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@ -1411,6 +1722,10 @@
"node": ">= 0.12.0"
}
},
"node_modules/codeflash": {
"resolved": "../packages/codeflash",
"link": true
},
"node_modules/collect-v8-coverage": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz",
@ -1522,6 +1837,13 @@
}
}
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true,
"license": "MIT"
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@ -1609,6 +1931,176 @@
"node": ">=8"
}
},
"node_modules/eslint": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.1",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.39.2",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.4.0",
"eslint-visitor-keys": "^4.2.1",
"espree": "^10.4.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^8.0.0",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
"bin": {
"eslint": "bin/eslint.js"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://eslint.org/donate"
},
"peerDependencies": {
"jiti": "*"
},
"peerDependenciesMeta": {
"jiti": {
"optional": true
}
}
},
"node_modules/eslint-scope": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-visitor-keys": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"license": "MIT",
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"p-locate": "^5.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/p-locate": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
"p-limit": "^3.0.2"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/espree": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@ -1623,6 +2115,52 @@
"node": ">=4"
}
},
"node_modules/esquery": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"estraverse": "^5.1.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
},
"engines": {
"node": ">=4.0"
}
},
"node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
}
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@ -1673,6 +2211,13 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@ -1680,6 +2225,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true,
"license": "MIT"
},
"node_modules/fb-watchman": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
@ -1690,6 +2242,19 @@
"bser": "2.1.1"
}
},
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"flat-cache": "^4.0.0"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@ -1717,6 +2282,27 @@
"node": ">=8"
}
},
"node_modules/flat-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"flatted": "^3.2.9",
"keyv": "^4.5.4"
},
"engines": {
"node": ">=16"
}
},
"node_modules/flatted": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"dev": true,
"license": "ISC"
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -1814,6 +2400,32 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/globals": {
"version": "17.2.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.2.0.tgz",
"integrity": "sha512-tovnCz/fEq+Ripoq+p/gN1u7l6A7wwkoBT9pRCzTHzsD/LvADIzXZdjmRymh5Ztf0DYC3Rwg5cZRYjxzBmzbWg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@ -1861,6 +2473,43 @@
"node": ">=10.17.0"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/import-fresh/node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/import-local": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
@ -1933,6 +2582,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@ -1953,6 +2612,19 @@
"node": ">=6"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -2713,6 +3385,13 @@
"node": ">=6"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true,
"license": "MIT"
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@ -2720,6 +3399,20 @@
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@ -2733,6 +3426,16 @@
"node": ">=6"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
}
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@ -2753,6 +3456,20 @@
"node": ">=6"
}
},
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@ -2773,6 +3490,13 @@
"node": ">=8"
}
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@ -2956,6 +3680,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
"license": "MIT",
"dependencies": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
"type-check": "^0.4.0",
"word-wrap": "^1.2.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -3011,6 +3753,19 @@
"node": ">=6"
}
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -3110,6 +3865,16 @@
"node": ">=8"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@ -3152,6 +3917,16 @@
"node": ">= 6"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/pure-rand": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
@ -3474,6 +4249,19 @@
"node": ">=8.0"
}
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
@ -3535,6 +4323,16 @@
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@ -3586,6 +4384,16 @@
"node": ">= 8"
}
},
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",

View file

@ -10,16 +10,14 @@
"devDependencies": {
"@eslint/js": "^9.39.2",
"codeflash": "file:../packages/codeflash",
"jest": "^29.7.0",
"globals": "^17.1.0",
"eslint": "^9.39.2",
"globals": "^17.1.0",
"jest": "^29.7.0",
"jest-junit": "^16.0.0"
},
"codeflash": {
"moduleRoot": ".",
"formatterCmds": [
"npx eslint --fix $file"
],
"language": "javascript"
"testsRoot": "tests",
"disableTelemetry": true
}
}

View file

@ -0,0 +1,85 @@
/**
* Tests for async utility functions - ES Module
*/
import { delay, processItemsSequential, asyncMap, asyncFilter } from '../async_utils.js';
describe('processItemsSequential', () => {
test('processes all items', async () => {
const items = [1, 2, 3, 4, 5];
const processor = async (x) => x * 2;
const results = await processItemsSequential(items, processor);
expect(results).toEqual([2, 4, 6, 8, 10]);
});
test('handles empty array', async () => {
const results = await processItemsSequential([], async (x) => x);
expect(results).toEqual([]);
});
test('handles async operations with delays', async () => {
const items = [1, 2, 3];
const processor = async (x) => {
await delay(1);
return x + 10;
};
const results = await processItemsSequential(items, processor);
expect(results).toEqual([11, 12, 13]);
});
test('preserves order', async () => {
const items = [5, 4, 3, 2, 1];
const processor = async (x) => x.toString();
const results = await processItemsSequential(items, processor);
expect(results).toEqual(['5', '4', '3', '2', '1']);
});
test('handles larger arrays', async () => {
const items = Array.from({ length: 20 }, (_, i) => i);
const processor = async (x) => x * 2;
const results = await processItemsSequential(items, processor);
expect(results.length).toBe(20);
expect(results[0]).toBe(0);
expect(results[19]).toBe(38);
});
});
describe('asyncMap', () => {
test('maps all items', async () => {
const items = [1, 2, 3];
const mapper = async (x) => x * 10;
const results = await asyncMap(items, mapper);
expect(results).toEqual([10, 20, 30]);
});
test('handles empty array', async () => {
const results = await asyncMap([], async (x) => x);
expect(results).toEqual([]);
});
test('handles objects', async () => {
const items = [{ a: 1 }, { a: 2 }];
const mapper = async (obj) => ({ ...obj, b: obj.a * 2 });
const results = await asyncMap(items, mapper);
expect(results).toEqual([{ a: 1, b: 2 }, { a: 2, b: 4 }]);
});
});
describe('asyncFilter', () => {
test('filters items based on predicate', async () => {
const items = [1, 2, 3, 4, 5, 6];
const predicate = async (x) => x % 2 === 0;
const results = await asyncFilter(items, predicate);
expect(results).toEqual([2, 4, 6]);
});
test('handles empty array', async () => {
const results = await asyncFilter([], async () => true);
expect(results).toEqual([]);
});
test('handles all items filtered out', async () => {
const items = [1, 2, 3];
const results = await asyncFilter(items, async () => false);
expect(results).toEqual([]);
});
});

View file

@ -0,0 +1,88 @@
/**
* DataProcessor class - demonstrates class method optimization in TypeScript.
* Contains intentionally inefficient implementations for optimization testing.
*/
/**
* A class for processing data arrays with various operations.
*/
export class DataProcessor<T> {
private data: T[];
/**
* Create a DataProcessor instance.
* @param data - Initial data array
*/
constructor(data: T[] = []) {
this.data = [...data];
}
/**
* Find duplicates in the data array.
* Intentionally inefficient O(n²) implementation.
* @returns Array of duplicate values
*/
findDuplicates(): T[] {
const duplicates: T[] = [];
for (let i = 0; i < this.data.length; i++) {
for (let j = i + 1; j < this.data.length; j++) {
if (this.data[i] === this.data[j]) {
if (!duplicates.includes(this.data[i])) {
duplicates.push(this.data[i]);
}
}
}
}
return duplicates;
}
/**
* Sort the data using bubble sort.
* Intentionally inefficient O(n²) implementation.
* @returns Sorted copy of the data
*/
sortData(): T[] {
const result = [...this.data];
const n = result.length;
for (let i = 0; i < n; i++) {
for (let j = 0; j < n - 1; j++) {
if (result[j] > result[j + 1]) {
const temp = result[j];
result[j] = result[j + 1];
result[j + 1] = temp;
}
}
}
return result;
}
/**
* Get unique values from the data.
* Intentionally inefficient O(n²) implementation.
* @returns Array of unique values
*/
getUnique(): T[] {
const unique: T[] = [];
for (let i = 0; i < this.data.length; i++) {
let found = false;
for (let j = 0; j < unique.length; j++) {
if (unique[j] === this.data[i]) {
found = true;
break;
}
}
if (!found) {
unique.push(this.data[i]);
}
}
return unique;
}
/**
* Get the data array.
* @returns The data array
*/
getData(): T[] {
return [...this.data];
}
}

View file

@ -8,6 +8,10 @@
"test:coverage": "jest --coverage",
"build": "tsc"
},
"codeflash": {
"moduleRoot": ".",
"testsRoot": "tests"
},
"keywords": [
"codeflash",
"optimization",

View file

@ -4,14 +4,21 @@
/**
* Reverse a string character by character.
* This is intentionally inefficient - uses string concatenation in a loop.
* This is intentionally inefficient O(n²) - rebuilds result string each iteration.
* @param str - The string to reverse
* @returns The reversed string
*/
export function reverseString(str: string): string {
// Intentionally inefficient O(n²) implementation for testing
let result = '';
for (let i = str.length - 1; i >= 0; i--) {
result += str[i];
// Rebuild the entire result string each iteration (very inefficient)
let temp = '';
for (let j = 0; j < result.length; j++) {
temp += result[j];
}
temp += str[i];
result = temp;
}
return result;
}

View file

@ -34,6 +34,29 @@ describe('bubbleSort', () => {
bubbleSort(original);
expect(original).toEqual([3, 1, 2]);
});
test('sorts a larger reverse sorted array for performance', () => {
const input: number[] = [];
for (let i = 500; i >= 0; i--) {
input.push(i);
}
const result = bubbleSort(input);
expect(result[0]).toBe(0);
expect(result[result.length - 1]).toBe(500);
});
test('sorts a larger random array for performance', () => {
const input = [
42, 17, 93, 8, 67, 31, 55, 22, 89, 4,
76, 12, 39, 58, 95, 26, 71, 48, 83, 19,
64, 3, 88, 37, 52, 11, 79, 46, 91, 28,
63, 7, 84, 33, 57, 14, 72, 41, 96, 24,
69, 6, 81, 36, 54, 16, 77, 44, 90, 29
];
const result = bubbleSort(input);
expect(result[0]).toBe(3);
expect(result[result.length - 1]).toBe(96);
});
});
describe('bubbleSortDescending', () => {

View file

@ -0,0 +1,95 @@
import { DataProcessor } from '../data_processor';
describe('DataProcessor', () => {
describe('findDuplicates', () => {
test('finds duplicates in array with repeated values', () => {
const processor = new DataProcessor([1, 2, 3, 2, 4, 3, 5]);
expect(processor.findDuplicates().sort()).toEqual([2, 3]);
});
test('returns empty array when no duplicates', () => {
const processor = new DataProcessor([1, 2, 3, 4, 5]);
expect(processor.findDuplicates()).toEqual([]);
});
test('handles empty array', () => {
const processor = new DataProcessor<number>([]);
expect(processor.findDuplicates()).toEqual([]);
});
test('handles array with all same values', () => {
const processor = new DataProcessor([5, 5, 5, 5]);
expect(processor.findDuplicates()).toEqual([5]);
});
test('handles larger arrays with duplicates', () => {
const data: number[] = [];
for (let i = 0; i < 100; i++) {
data.push(i % 20);
}
const processor = new DataProcessor(data);
const duplicates = processor.findDuplicates();
expect(duplicates.length).toBe(20);
});
});
describe('sortData', () => {
test('sorts numbers in ascending order', () => {
const processor = new DataProcessor([5, 2, 8, 1, 9]);
expect(processor.sortData()).toEqual([1, 2, 5, 8, 9]);
});
test('handles already sorted array', () => {
const processor = new DataProcessor([1, 2, 3, 4, 5]);
expect(processor.sortData()).toEqual([1, 2, 3, 4, 5]);
});
test('handles reverse sorted array', () => {
const processor = new DataProcessor([5, 4, 3, 2, 1]);
expect(processor.sortData()).toEqual([1, 2, 3, 4, 5]);
});
test('handles array with duplicates', () => {
const processor = new DataProcessor([3, 1, 4, 1, 5, 9, 2, 6, 5]);
expect(processor.sortData()).toEqual([1, 1, 2, 3, 4, 5, 5, 6, 9]);
});
test('handles larger arrays', () => {
const data: number[] = [];
for (let i = 500; i >= 0; i--) {
data.push(i);
}
const processor = new DataProcessor(data);
const sorted = processor.sortData();
expect(sorted[0]).toBe(0);
expect(sorted[sorted.length - 1]).toBe(500);
});
});
describe('getUnique', () => {
test('returns unique values', () => {
const processor = new DataProcessor([1, 2, 2, 3, 3, 3, 4]);
expect(processor.getUnique()).toEqual([1, 2, 3, 4]);
});
test('preserves order of first occurrence', () => {
const processor = new DataProcessor([3, 1, 2, 1, 3, 2]);
expect(processor.getUnique()).toEqual([3, 1, 2]);
});
test('handles empty array', () => {
const processor = new DataProcessor<number>([]);
expect(processor.getUnique()).toEqual([]);
});
test('handles array with all unique values', () => {
const processor = new DataProcessor([1, 2, 3, 4, 5]);
expect(processor.getUnique()).toEqual([1, 2, 3, 4, 5]);
});
test('handles strings', () => {
const processor = new DataProcessor(['a', 'b', 'a', 'c', 'b']);
expect(processor.getUnique()).toEqual(['a', 'b', 'c']);
});
});
});

View file

@ -20,6 +20,20 @@ describe('reverseString', () => {
test('handles special characters', () => {
expect(reverseString('a!b@c#')).toBe('#c@b!a');
});
test('reverses a longer string for performance', () => {
const input = 'abcdefghijklmnopqrstuvwxyz'.repeat(20);
const result = reverseString(input);
expect(result.length).toBe(input.length);
expect(result[0]).toBe('z');
expect(result[result.length - 1]).toBe('a');
});
test('reverses a medium string', () => {
const input = 'The quick brown fox jumps over the lazy dog';
const expected = 'god yzal eht revo spmuj xof nworb kciuq ehT';
expect(reverseString(input)).toBe(expected);
});
});
describe('isPalindrome', () => {

View file

@ -1,2 +1,2 @@
# These version placeholders will be replaced by uv-dynamic-versioning during build.
__version__ = "0.20.0"
__version__ = "0.20.0.post91.dev0+28f8eb18"

View file

@ -0,0 +1,862 @@
"""Tests for JavaScript/TypeScript configuration detection and parsing."""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from codeflash.code_utils.config_js import (
PACKAGE_JSON_CACHE,
PACKAGE_JSON_DATA_CACHE,
clear_cache,
detect_formatter,
detect_language,
detect_module_root,
detect_test_runner,
find_package_json,
get_package_json_data,
parse_package_json_config,
)
@pytest.fixture(autouse=True)
def clear_caches() -> None:
"""Clear all caches before each test."""
clear_cache()
class TestGetPackageJsonData:
"""Tests for get_package_json_data function."""
def test_loads_valid_package_json(self, tmp_path: Path) -> None:
"""Should load and return valid package.json data."""
package_json = tmp_path / "package.json"
data = {"name": "test-project", "version": "1.0.0"}
package_json.write_text(json.dumps(data))
result = get_package_json_data(package_json)
assert result == data
def test_caches_loaded_data(self, tmp_path: Path) -> None:
"""Should cache package.json data after first load."""
package_json = tmp_path / "package.json"
data = {"name": "test-project"}
package_json.write_text(json.dumps(data))
# First call
result1 = get_package_json_data(package_json)
# Modify file
package_json.write_text(json.dumps({"name": "modified"}))
# Second call should return cached data
result2 = get_package_json_data(package_json)
assert result1 == result2 == data
def test_returns_none_for_invalid_json(self, tmp_path: Path) -> None:
"""Should return None for invalid JSON."""
package_json = tmp_path / "package.json"
package_json.write_text("{ invalid json }")
result = get_package_json_data(package_json)
assert result is None
def test_returns_none_for_nonexistent_file(self, tmp_path: Path) -> None:
"""Should return None for non-existent file."""
package_json = tmp_path / "package.json"
result = get_package_json_data(package_json)
assert result is None
def test_returns_none_for_unreadable_file(self, tmp_path: Path) -> None:
"""Should return None if file cannot be read."""
package_json = tmp_path / "package.json"
package_json.write_text("{}")
package_json.chmod(0o000)
try:
result = get_package_json_data(package_json)
assert result is None
finally:
package_json.chmod(0o644)
class TestDetectLanguage:
"""Tests for detect_language function."""
def test_detects_typescript_with_tsconfig(self, tmp_path: Path) -> None:
"""Should detect TypeScript when tsconfig.json exists."""
(tmp_path / "tsconfig.json").write_text("{}")
result = detect_language(tmp_path)
assert result == "typescript"
def test_detects_javascript_without_tsconfig(self, tmp_path: Path) -> None:
"""Should detect JavaScript when no tsconfig.json exists."""
result = detect_language(tmp_path)
assert result == "javascript"
def test_detects_typescript_with_complex_tsconfig(self, tmp_path: Path) -> None:
"""Should detect TypeScript even with complex tsconfig."""
tsconfig = {
"compilerOptions": {"target": "ES2020", "module": "commonjs"},
"include": ["src/**/*"],
}
(tmp_path / "tsconfig.json").write_text(json.dumps(tsconfig))
result = detect_language(tmp_path)
assert result == "typescript"
class TestDetectModuleRoot:
"""Tests for detect_module_root function."""
def test_detects_from_exports_string(self, tmp_path: Path) -> None:
"""Should detect module root from exports string field."""
(tmp_path / "lib").mkdir()
package_data = {"exports": "./lib/index.js"}
result = detect_module_root(tmp_path, package_data)
assert result == "lib"
def test_detects_from_exports_object_dot(self, tmp_path: Path) -> None:
"""Should detect module root from exports object with '.' key."""
(tmp_path / "dist").mkdir()
package_data = {"exports": {".": "./dist/index.js"}}
result = detect_module_root(tmp_path, package_data)
assert result == "dist"
def test_detects_from_exports_object_nested(self, tmp_path: Path) -> None:
"""Should detect module root from nested exports object."""
(tmp_path / "src").mkdir()
package_data = {"exports": {".": {"import": "./src/index.mjs", "require": "./src/index.cjs"}}}
result = detect_module_root(tmp_path, package_data)
assert result == "src"
def test_detects_from_exports_import_key(self, tmp_path: Path) -> None:
"""Should detect from exports with direct import key."""
(tmp_path / "esm").mkdir()
package_data = {"exports": {"import": "./esm/index.js"}}
result = detect_module_root(tmp_path, package_data)
assert result == "esm"
def test_detects_from_module_field(self, tmp_path: Path) -> None:
"""Should detect module root from module field (ESM entry)."""
(tmp_path / "es").mkdir()
package_data = {"module": "./es/index.js"}
result = detect_module_root(tmp_path, package_data)
assert result == "es"
def test_detects_from_main_field(self, tmp_path: Path) -> None:
"""Should detect module root from main field (CJS entry)."""
(tmp_path / "lib").mkdir()
package_data = {"main": "./lib/index.js"}
result = detect_module_root(tmp_path, package_data)
assert result == "lib"
def test_prefers_exports_over_module(self, tmp_path: Path) -> None:
"""Should prefer exports field over module field."""
(tmp_path / "exports-dir").mkdir()
(tmp_path / "module-dir").mkdir()
package_data = {"exports": "./exports-dir/index.js", "module": "./module-dir/index.js"}
result = detect_module_root(tmp_path, package_data)
assert result == "exports-dir"
def test_prefers_module_over_main(self, tmp_path: Path) -> None:
"""Should prefer module field over main field."""
(tmp_path / "esm").mkdir()
(tmp_path / "cjs").mkdir()
package_data = {"module": "./esm/index.js", "main": "./cjs/index.js"}
result = detect_module_root(tmp_path, package_data)
assert result == "esm"
def test_detects_src_directory_convention(self, tmp_path: Path) -> None:
"""Should detect src/ directory when no package.json fields point elsewhere."""
(tmp_path / "src").mkdir()
package_data = {}
result = detect_module_root(tmp_path, package_data)
assert result == "src"
def test_falls_back_to_current_directory(self, tmp_path: Path) -> None:
"""Should fall back to '.' when nothing else matches."""
package_data = {}
result = detect_module_root(tmp_path, package_data)
assert result == "."
def test_ignores_nonexistent_directory_from_exports(self, tmp_path: Path) -> None:
"""Should ignore exports pointing to non-existent directory."""
(tmp_path / "src").mkdir()
package_data = {"exports": "./nonexistent/index.js"}
result = detect_module_root(tmp_path, package_data)
assert result == "src"
def test_ignores_root_level_main(self, tmp_path: Path) -> None:
"""Should ignore main that points to root level file."""
(tmp_path / "src").mkdir()
package_data = {"main": "./index.js"}
result = detect_module_root(tmp_path, package_data)
assert result == "src"
def test_handles_deeply_nested_exports(self, tmp_path: Path) -> None:
"""Should handle deeply nested export paths."""
(tmp_path / "packages" / "core" / "dist").mkdir(parents=True)
package_data = {"exports": {".": {"import": "./packages/core/dist/index.mjs"}}}
result = detect_module_root(tmp_path, package_data)
assert result == "packages/core/dist"
def test_handles_empty_exports(self, tmp_path: Path) -> None:
"""Should handle empty exports gracefully."""
(tmp_path / "src").mkdir()
package_data = {"exports": {}}
result = detect_module_root(tmp_path, package_data)
assert result == "src"
def test_handles_null_exports(self, tmp_path: Path) -> None:
"""Should handle null/None exports gracefully."""
package_data = {"exports": None}
result = detect_module_root(tmp_path, package_data)
assert result == "."
class TestDetectTestRunner:
"""Tests for detect_test_runner function."""
def test_detects_vitest_from_dev_dependencies(self, tmp_path: Path) -> None:
"""Should detect vitest from devDependencies."""
package_data = {"devDependencies": {"vitest": "^1.0.0"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "vitest"
def test_detects_jest_from_dev_dependencies(self, tmp_path: Path) -> None:
"""Should detect jest from devDependencies."""
package_data = {"devDependencies": {"jest": "^29.0.0"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_detects_mocha_from_dev_dependencies(self, tmp_path: Path) -> None:
"""Should detect mocha from devDependencies."""
package_data = {"devDependencies": {"mocha": "^10.0.0"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "mocha"
def test_detects_from_dependencies(self, tmp_path: Path) -> None:
"""Should also check dependencies (not just devDependencies)."""
package_data = {"dependencies": {"jest": "^29.0.0"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_prefers_vitest_over_jest(self, tmp_path: Path) -> None:
"""Should prefer vitest when both are present."""
package_data = {"devDependencies": {"vitest": "^1.0.0", "jest": "^29.0.0"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "vitest"
def test_prefers_jest_over_mocha(self, tmp_path: Path) -> None:
"""Should prefer jest over mocha."""
package_data = {"devDependencies": {"jest": "^29.0.0", "mocha": "^10.0.0"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_detects_vitest_from_test_script(self, tmp_path: Path) -> None:
"""Should detect vitest from scripts.test."""
package_data = {"scripts": {"test": "vitest run"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "vitest"
def test_detects_jest_from_test_script(self, tmp_path: Path) -> None:
"""Should detect jest from scripts.test."""
package_data = {"scripts": {"test": "jest --coverage"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_detects_mocha_from_test_script(self, tmp_path: Path) -> None:
"""Should detect mocha from scripts.test."""
package_data = {"scripts": {"test": "mocha tests/**/*.js"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "mocha"
def test_detects_from_npx_command(self, tmp_path: Path) -> None:
"""Should detect runner from npx command in test script."""
package_data = {"scripts": {"test": "npx jest"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_detects_case_insensitive(self, tmp_path: Path) -> None:
"""Should detect runner case-insensitively from scripts."""
package_data = {"scripts": {"test": "JEST --ci"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_prefers_deps_over_scripts(self, tmp_path: Path) -> None:
"""Should prefer devDependencies detection over scripts."""
package_data = {"devDependencies": {"vitest": "^1.0.0"}, "scripts": {"test": "jest"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "vitest"
def test_defaults_to_jest(self, tmp_path: Path) -> None:
"""Should default to jest when nothing is detected."""
package_data = {}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_handles_complex_test_script(self, tmp_path: Path) -> None:
"""Should detect from complex test scripts."""
package_data = {"scripts": {"test": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage"}}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_handles_missing_scripts(self, tmp_path: Path) -> None:
"""Should handle missing scripts gracefully."""
package_data = {"name": "test"}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
def test_handles_non_string_test_script(self, tmp_path: Path) -> None:
"""Should handle non-string test script gracefully."""
package_data = {"scripts": {"test": 123}}
result = detect_test_runner(tmp_path, package_data)
assert result == "jest"
class TestDetectFormatter:
"""Tests for detect_formatter function."""
def test_detects_prettier_from_dev_dependencies(self, tmp_path: Path) -> None:
"""Should detect prettier from devDependencies."""
package_data = {"devDependencies": {"prettier": "^3.0.0"}}
result = detect_formatter(tmp_path, package_data)
assert result == ["npx prettier --write $file"]
def test_detects_eslint_from_dev_dependencies(self, tmp_path: Path) -> None:
"""Should detect eslint from devDependencies."""
package_data = {"devDependencies": {"eslint": "^8.0.0"}}
result = detect_formatter(tmp_path, package_data)
assert result == ["npx eslint --fix $file"]
def test_detects_from_dependencies(self, tmp_path: Path) -> None:
"""Should also check dependencies."""
package_data = {"dependencies": {"prettier": "^3.0.0"}}
result = detect_formatter(tmp_path, package_data)
assert result == ["npx prettier --write $file"]
def test_prefers_prettier_over_eslint(self, tmp_path: Path) -> None:
"""Should prefer prettier when both are present."""
package_data = {"devDependencies": {"prettier": "^3.0.0", "eslint": "^8.0.0"}}
result = detect_formatter(tmp_path, package_data)
assert result == ["npx prettier --write $file"]
def test_returns_none_when_no_formatter(self, tmp_path: Path) -> None:
"""Should return None when no formatter is detected."""
package_data = {"devDependencies": {"typescript": "^5.0.0"}}
result = detect_formatter(tmp_path, package_data)
assert result is None
def test_returns_none_for_empty_deps(self, tmp_path: Path) -> None:
"""Should return None for empty dependencies."""
package_data = {}
result = detect_formatter(tmp_path, package_data)
assert result is None
def test_detects_eslint_related_packages(self, tmp_path: Path) -> None:
"""Should detect eslint even with scoped packages."""
package_data = {"devDependencies": {"eslint": "^8.0.0", "@eslint/js": "^8.0.0"}}
result = detect_formatter(tmp_path, package_data)
assert result == ["npx eslint --fix $file"]
class TestFindPackageJson:
"""Tests for find_package_json function."""
def test_finds_explicit_package_json(self, tmp_path: Path) -> None:
"""Should find explicitly provided package.json path."""
package_json = tmp_path / "package.json"
package_json.write_text("{}")
result = find_package_json(package_json)
assert result == package_json
def test_returns_none_for_wrong_filename(self, tmp_path: Path) -> None:
"""Should return None if explicit path is not package.json."""
other_file = tmp_path / "other.json"
other_file.write_text("{}")
result = find_package_json(other_file)
assert result is None
def test_returns_none_for_nonexistent_explicit(self, tmp_path: Path) -> None:
"""Should return None if explicit package.json doesn't exist."""
package_json = tmp_path / "package.json"
result = find_package_json(package_json)
assert result is None
class TestParsePackageJsonConfig:
"""Tests for parse_package_json_config function."""
def test_parses_minimal_package_json(self, tmp_path: Path) -> None:
"""Should parse package.json without codeflash section."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "devDependencies": {"jest": "^29.0.0"}}))
result = parse_package_json_config(package_json)
assert result is not None
config, path = result
assert config["language"] == "javascript"
assert config["test_runner"] == "jest"
assert config["pytest_cmd"] == "jest"
assert path == package_json
def test_parses_typescript_project(self, tmp_path: Path) -> None:
"""Should detect TypeScript project."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test"}))
(tmp_path / "tsconfig.json").write_text("{}")
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["language"] == "typescript"
def test_auto_detects_module_root(self, tmp_path: Path) -> None:
"""Should auto-detect module root from package.json."""
(tmp_path / "src").mkdir()
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "main": "./src/index.js"}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["module_root"] == str((tmp_path / "src").resolve())
def test_respects_module_root_override(self, tmp_path: Path) -> None:
"""Should respect moduleRoot override in codeflash config."""
(tmp_path / "lib").mkdir()
(tmp_path / "src").mkdir()
package_json = tmp_path / "package.json"
package_json.write_text(
json.dumps({"name": "test", "main": "./src/index.js", "codeflash": {"moduleRoot": "lib"}})
)
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["module_root"] == str((tmp_path / "lib").resolve())
def test_auto_detects_formatter(self, tmp_path: Path) -> None:
"""Should auto-detect formatter from devDependencies."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "devDependencies": {"prettier": "^3.0.0"}}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["formatter_cmds"] == ["npx prettier --write $file"]
def test_respects_formatter_override(self, tmp_path: Path) -> None:
"""Should respect formatterCmds override."""
package_json = tmp_path / "package.json"
package_json.write_text(
json.dumps(
{
"name": "test",
"devDependencies": {"prettier": "^3.0.0"},
"codeflash": {"formatterCmds": ["custom-formatter $file"]},
}
)
)
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["formatter_cmds"] == ["custom-formatter $file"]
def test_parses_ignore_paths(self, tmp_path: Path) -> None:
"""Should parse ignorePaths from codeflash config."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "codeflash": {"ignorePaths": ["dist", "node_modules"]}}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert str((tmp_path / "dist").resolve()) in config["ignore_paths"]
assert str((tmp_path / "node_modules").resolve()) in config["ignore_paths"]
def test_parses_benchmarks_root(self, tmp_path: Path) -> None:
"""Should parse benchmarksRoot from codeflash config."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "codeflash": {"benchmarksRoot": "__benchmarks__"}}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["benchmarks_root"] == str((tmp_path / "__benchmarks__").resolve())
def test_parses_disable_telemetry(self, tmp_path: Path) -> None:
"""Should parse disableTelemetry from codeflash config."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "codeflash": {"disableTelemetry": True}}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["disable_telemetry"] is True
def test_defaults_disable_telemetry_to_false(self, tmp_path: Path) -> None:
"""Should default disableTelemetry to False."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test"}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["disable_telemetry"] is False
def test_sets_backwards_compat_defaults(self, tmp_path: Path) -> None:
"""Should set backwards compatibility defaults."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test"}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["git_remote"] == "origin"
assert config["disable_imports_sorting"] is False
assert config["override_fixtures"] is False
def test_returns_none_for_invalid_json(self, tmp_path: Path) -> None:
"""Should return None for invalid JSON."""
package_json = tmp_path / "package.json"
package_json.write_text("invalid json")
result = parse_package_json_config(package_json)
assert result is None
def test_handles_non_dict_codeflash_config(self, tmp_path: Path) -> None:
"""Should handle non-dict codeflash section."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "codeflash": "invalid"}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
# Should use auto-detected/default values
assert "language" in config
def test_empty_formatter_when_none_detected(self, tmp_path: Path) -> None:
"""Should have empty formatter_cmds when no formatter detected."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "devDependencies": {"typescript": "^5.0.0"}}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["formatter_cmds"] == []
def test_parses_git_remote_from_config(self, tmp_path: Path) -> None:
"""Should parse gitRemote from codeflash config."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "codeflash": {"gitRemote": "upstream"}}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["git_remote"] == "upstream"
def test_defaults_git_remote_to_origin(self, tmp_path: Path) -> None:
"""Should default gitRemote to 'origin' when not specified."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test"}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["git_remote"] == "origin"
def test_handles_empty_git_remote(self, tmp_path: Path) -> None:
"""Should handle empty gitRemote in config."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "codeflash": {"gitRemote": ""}}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
# Empty string should be treated as the value (not defaulted to origin)
assert config["git_remote"] == ""
class TestClearCache:
"""Tests for clear_cache function."""
def test_clears_both_caches(self, tmp_path: Path) -> None:
"""Should clear both path and data caches."""
# Populate caches
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test"}))
get_package_json_data(package_json)
assert len(PACKAGE_JSON_DATA_CACHE) > 0
clear_cache()
assert len(PACKAGE_JSON_CACHE) == 0
assert len(PACKAGE_JSON_DATA_CACHE) == 0
class TestRealWorldPackageJsonExamples:
"""Tests with real-world-like package.json configurations."""
def test_nextjs_project(self, tmp_path: Path) -> None:
"""Should handle Next.js project configuration."""
(tmp_path / "src").mkdir()
(tmp_path / "tsconfig.json").write_text("{}")
package_json = tmp_path / "package.json"
package_json.write_text(
json.dumps(
{
"name": "my-nextjs-app",
"scripts": {"test": "jest"},
"devDependencies": {"jest": "^29.0.0", "prettier": "^3.0.0", "typescript": "^5.0.0"},
}
)
)
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["language"] == "typescript"
assert config["module_root"] == str((tmp_path / "src").resolve())
assert config["test_runner"] == "jest"
assert config["formatter_cmds"] == ["npx prettier --write $file"]
def test_vite_react_project(self, tmp_path: Path) -> None:
"""Should handle Vite + React project configuration."""
(tmp_path / "src").mkdir()
(tmp_path / "tsconfig.json").write_text("{}")
package_json = tmp_path / "package.json"
package_json.write_text(
json.dumps(
{
"name": "vite-react-app",
"type": "module",
"scripts": {"test": "vitest"},
"devDependencies": {"vitest": "^1.0.0", "eslint": "^8.0.0"},
}
)
)
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["language"] == "typescript"
assert config["test_runner"] == "vitest"
assert config["formatter_cmds"] == ["npx eslint --fix $file"]
def test_library_with_exports(self, tmp_path: Path) -> None:
"""Should handle library with modern exports field."""
(tmp_path / "dist").mkdir()
package_json = tmp_path / "package.json"
package_json.write_text(
json.dumps(
{
"name": "my-library",
"exports": {".": {"import": "./dist/index.mjs", "require": "./dist/index.cjs"}},
"devDependencies": {"vitest": "^1.0.0", "prettier": "^3.0.0"},
}
)
)
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["module_root"] == str((tmp_path / "dist").resolve())
def test_monorepo_package(self, tmp_path: Path) -> None:
"""Should handle monorepo package configuration."""
(tmp_path / "packages" / "core" / "src").mkdir(parents=True)
package_json = tmp_path / "package.json"
package_json.write_text(
json.dumps(
{
"name": "@myorg/core",
"main": "./packages/core/src/index.js",
"devDependencies": {"jest": "^29.0.0"},
}
)
)
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["module_root"] == str((tmp_path / "packages/core/src").resolve())
def test_node_cli_project(self, tmp_path: Path) -> None:
"""Should handle Node.js CLI project."""
(tmp_path / "bin").mkdir()
(tmp_path / "lib").mkdir()
package_json = tmp_path / "package.json"
package_json.write_text(
json.dumps(
{
"name": "my-cli",
"bin": {"my-cli": "./bin/cli.js"},
"main": "./lib/index.js",
"devDependencies": {"mocha": "^10.0.0"},
}
)
)
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["module_root"] == str((tmp_path / "lib").resolve())
assert config["test_runner"] == "mocha"
def test_minimal_project(self, tmp_path: Path) -> None:
"""Should handle minimal package.json."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "minimal"}))
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["language"] == "javascript"
assert config["module_root"] == str(tmp_path.resolve())
assert config["test_runner"] == "jest"
assert config["formatter_cmds"] == []
def test_existing_codeflash_config_with_overrides(self, tmp_path: Path) -> None:
"""Should handle existing codeflash config with custom overrides."""
(tmp_path / "custom-src").mkdir()
package_json = tmp_path / "package.json"
package_json.write_text(
json.dumps(
{
"name": "configured-project",
"devDependencies": {"jest": "^29.0.0", "prettier": "^3.0.0"},
"codeflash": {
"moduleRoot": "custom-src",
"formatterCmds": ["npx prettier --write --single-quote $file"],
"ignorePaths": ["dist", "coverage"],
"disableTelemetry": True,
},
}
)
)
result = parse_package_json_config(package_json)
assert result is not None
config, _ = result
assert config["module_root"] == str((tmp_path / "custom-src").resolve())
assert config["formatter_cmds"] == ["npx prettier --write --single-quote $file"]
assert len(config["ignore_paths"]) == 2
assert config["disable_telemetry"] is True

View file

@ -0,0 +1,238 @@
"""Tests for JavaScript/TypeScript GitHub Actions workflow helpers."""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from codeflash.cli_cmds.init_javascript import (
JsPackageManager,
get_js_codeflash_install_step,
get_js_codeflash_run_command,
get_js_dependency_installation_commands,
get_js_runtime_setup_steps,
is_codeflash_dependency,
)
class TestIsCodeflashDependency:
"""Tests for is_codeflash_dependency function."""
def test_returns_true_when_in_dev_dependencies(self, tmp_path: Path) -> None:
"""Should return True when codeflash is in devDependencies."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"devDependencies": {"codeflash": "^1.0.0"}}))
assert is_codeflash_dependency(tmp_path) is True
def test_returns_true_when_in_dependencies(self, tmp_path: Path) -> None:
"""Should return True when codeflash is in dependencies."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"dependencies": {"codeflash": "^1.0.0"}}))
assert is_codeflash_dependency(tmp_path) is True
def test_returns_false_when_not_present(self, tmp_path: Path) -> None:
"""Should return False when codeflash is not in any dependencies."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"devDependencies": {"jest": "^29.0.0"}}))
assert is_codeflash_dependency(tmp_path) is False
def test_returns_false_when_no_package_json(self, tmp_path: Path) -> None:
"""Should return False when package.json doesn't exist."""
assert is_codeflash_dependency(tmp_path) is False
def test_returns_false_for_invalid_json(self, tmp_path: Path) -> None:
"""Should return False for invalid package.json."""
package_json = tmp_path / "package.json"
package_json.write_text("invalid json")
assert is_codeflash_dependency(tmp_path) is False
def test_handles_empty_dependencies(self, tmp_path: Path) -> None:
"""Should handle empty dependencies objects."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "test", "dependencies": {}, "devDependencies": {}}))
assert is_codeflash_dependency(tmp_path) is False
class TestGetJsRuntimeSetupSteps:
"""Tests for get_js_runtime_setup_steps function."""
def test_npm_setup(self) -> None:
"""Should generate correct setup steps for npm."""
result = get_js_runtime_setup_steps(JsPackageManager.NPM)
assert "Setup Node.js" in result
assert "actions/setup-node@v4" in result
assert "node-version: '22'" in result
assert "cache: 'npm'" in result
def test_yarn_setup(self) -> None:
"""Should generate correct setup steps for yarn."""
result = get_js_runtime_setup_steps(JsPackageManager.YARN)
assert "Setup Node.js" in result
assert "actions/setup-node@v4" in result
assert "cache: 'yarn'" in result
def test_pnpm_setup(self) -> None:
"""Should generate correct setup steps for pnpm."""
result = get_js_runtime_setup_steps(JsPackageManager.PNPM)
assert "Setup pnpm" in result
assert "pnpm/action-setup@v4" in result
assert "Setup Node.js" in result
assert "cache: 'pnpm'" in result
def test_bun_setup(self) -> None:
"""Should generate correct setup steps for bun."""
result = get_js_runtime_setup_steps(JsPackageManager.BUN)
assert "Setup Bun" in result
assert "oven-sh/setup-bun@v2" in result
assert "bun-version: latest" in result
def test_unknown_defaults_to_npm(self) -> None:
"""Should default to npm setup for unknown package manager."""
result = get_js_runtime_setup_steps(JsPackageManager.UNKNOWN)
assert "cache: 'npm'" in result
class TestGetJsDependencyInstallationCommands:
"""Tests for get_js_dependency_installation_commands function."""
def test_npm_install(self) -> None:
"""Should return npm ci for npm."""
assert get_js_dependency_installation_commands(JsPackageManager.NPM) == "npm ci"
def test_yarn_install(self) -> None:
"""Should return yarn install for yarn."""
assert get_js_dependency_installation_commands(JsPackageManager.YARN) == "yarn install"
def test_pnpm_install(self) -> None:
"""Should return pnpm install for pnpm."""
assert get_js_dependency_installation_commands(JsPackageManager.PNPM) == "pnpm install"
def test_bun_install(self) -> None:
"""Should return bun install for bun."""
assert get_js_dependency_installation_commands(JsPackageManager.BUN) == "bun install"
class TestGetJsCodeflashInstallStep:
"""Tests for get_js_codeflash_install_step function."""
def test_returns_empty_when_is_dependency(self) -> None:
"""Should return empty string when codeflash is a dependency."""
result = get_js_codeflash_install_step(JsPackageManager.NPM, is_dependency=True)
assert result == ""
def test_npm_global_install(self) -> None:
"""Should generate npm global install when not a dependency."""
result = get_js_codeflash_install_step(JsPackageManager.NPM, is_dependency=False)
assert "Install Codeflash" in result
assert "npm install -g codeflash" in result
def test_yarn_global_install(self) -> None:
"""Should generate yarn global install when not a dependency."""
result = get_js_codeflash_install_step(JsPackageManager.YARN, is_dependency=False)
assert "yarn global add codeflash" in result
def test_pnpm_global_install(self) -> None:
"""Should generate pnpm global install when not a dependency."""
result = get_js_codeflash_install_step(JsPackageManager.PNPM, is_dependency=False)
assert "pnpm add -g codeflash" in result
def test_bun_global_install(self) -> None:
"""Should generate bun global install when not a dependency."""
result = get_js_codeflash_install_step(JsPackageManager.BUN, is_dependency=False)
assert "bun add -g codeflash" in result
class TestGetJsCodeflashRunCommand:
"""Tests for get_js_codeflash_run_command function."""
def test_npm_with_dependency(self) -> None:
"""Should use npx when codeflash is a dependency."""
result = get_js_codeflash_run_command(JsPackageManager.NPM, is_dependency=True)
assert result == "npx codeflash"
def test_npm_without_dependency(self) -> None:
"""Should use direct codeflash when globally installed."""
result = get_js_codeflash_run_command(JsPackageManager.NPM, is_dependency=False)
assert result == "codeflash"
def test_yarn_with_dependency(self) -> None:
"""Should use yarn codeflash when it's a dependency."""
result = get_js_codeflash_run_command(JsPackageManager.YARN, is_dependency=True)
assert result == "yarn codeflash"
def test_pnpm_with_dependency(self) -> None:
"""Should use pnpm exec when it's a dependency."""
result = get_js_codeflash_run_command(JsPackageManager.PNPM, is_dependency=True)
assert result == "pnpm exec codeflash"
def test_bun_with_dependency(self) -> None:
"""Should use bun run when it's a dependency."""
result = get_js_codeflash_run_command(JsPackageManager.BUN, is_dependency=True)
assert result == "bun run codeflash"
def test_all_global_installs_use_direct_command(self) -> None:
"""All package managers should use direct 'codeflash' when globally installed."""
for pm in [JsPackageManager.NPM, JsPackageManager.YARN, JsPackageManager.PNPM, JsPackageManager.BUN]:
result = get_js_codeflash_run_command(pm, is_dependency=False)
assert result == "codeflash", f"Failed for {pm}"
class TestWorkflowTemplateIntegration:
"""Integration tests for workflow template generation."""
def test_workflow_template_exists(self) -> None:
"""Verify the JS workflow template file exists."""
from importlib.resources import files
template_path = files("codeflash").joinpath("cli_cmds", "workflows", "codeflash-optimize-js.yaml")
content = template_path.read_text(encoding="utf-8")
# Check all placeholders exist
assert "{{ codeflash_module_path }}" in content
assert "{{ working_directory }}" in content
assert "{{ setup_runtime_steps }}" in content
assert "{{ install_dependencies_command }}" in content
assert "{{ install_codeflash_step }}" in content
assert "{{ codeflash_command }}" in content
def test_workflow_template_has_correct_structure(self) -> None:
"""Verify the JS workflow template has the expected YAML structure."""
from importlib.resources import files
template_path = files("codeflash").joinpath("cli_cmds", "workflows", "codeflash-optimize-js.yaml")
content = template_path.read_text(encoding="utf-8")
# Check key sections
assert "name: Codeflash" in content
assert "pull_request:" in content
assert "workflow_dispatch:" in content
assert "concurrency:" in content
assert "cancel-in-progress: true" in content
assert "jobs:" in content
assert "optimize:" in content
assert "github.actor != 'codeflash-ai[bot]'" in content
assert "CODEFLASH_API_KEY" in content
assert "actions/checkout@v4" in content
assert "fetch-depth: 0" in content

View file

@ -0,0 +1,32 @@
"""End-to-end test for JavaScript CommonJS optimization.
Tests optimization of a simple recursive fibonacci function using CommonJS module format.
"""
import pathlib
from end_to_end_test_utilities_js import JSTestConfig, run_js_codeflash_command, run_with_retries
def run_test() -> bool:
"""Run the CommonJS fibonacci optimization test."""
config = JSTestConfig(
file_path=pathlib.Path("fibonacci.js"),
function_name="fibonacci",
min_improvement_x=0.5, # Expect at least 50% improvement
expected_improvement_pct=50,
expected_test_files=1,
)
cwd = (
pathlib.Path(__file__).parent.parent.parent
/ "code_to_optimize"
/ "js"
/ "code_to_optimize_js"
).resolve()
return run_js_codeflash_command(cwd, config)
if __name__ == "__main__":
exit(run_with_retries(run_test))

View file

@ -0,0 +1,33 @@
"""End-to-end test for JavaScript ES Modules async function optimization.
Tests optimization of async functions using ES Module format.
This tests the ESM module system with async/await code patterns.
"""
import pathlib
from end_to_end_test_utilities_js import JSTestConfig, run_js_codeflash_command, run_with_retries
def run_test() -> bool:
"""Run the ES Modules async function optimization test."""
config = JSTestConfig(
file_path=pathlib.Path("async_utils.js"),
function_name="processItemsSequential",
min_improvement_x=0.1, # Async optimizations may have variable gains
expected_improvement_pct=10,
expected_test_files=1,
)
cwd = (
pathlib.Path(__file__).parent.parent.parent
/ "code_to_optimize"
/ "js"
/ "code_to_optimize_js_esm"
).resolve()
return run_js_codeflash_command(cwd, config)
if __name__ == "__main__":
exit(run_with_retries(run_test))

View file

@ -0,0 +1,32 @@
"""End-to-end test for TypeScript class method optimization.
Tests optimization of class methods in TypeScript.
"""
import pathlib
from end_to_end_test_utilities_js import JSTestConfig, run_js_codeflash_command, run_with_retries
def run_test() -> bool:
"""Run the TypeScript class method optimization test."""
config = JSTestConfig(
file_path=pathlib.Path("data_processor.ts"),
function_name="DataProcessor.findDuplicates",
min_improvement_x=0.3, # Expect at least 30% improvement
expected_improvement_pct=30,
expected_test_files=1,
)
cwd = (
pathlib.Path(__file__).parent.parent.parent
/ "code_to_optimize"
/ "js"
/ "code_to_optimize_ts"
).resolve()
return run_js_codeflash_command(cwd, config)
if __name__ == "__main__":
exit(run_with_retries(run_test))

View file

@ -0,0 +1,213 @@
"""End-to-end test utilities for JavaScript/TypeScript optimization testing.
Similar to end_to_end_test_utilities.py but adapted for JS/TS projects.
"""
import logging
import os
import pathlib
import re
import shutil
import subprocess
import time
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class JSTestConfig:
"""Configuration for a JavaScript/TypeScript e2e test."""
# Path to the source file to optimize (relative to project root)
file_path: pathlib.Path
# Function name to optimize (optional - if not specified, optimizes all in file)
function_name: Optional[str] = None
# Minimum improvement multiplier (e.g., 0.5 = 50% faster)
min_improvement_x: float = 0.1
# Expected improvement percentage (optimization must exceed this)
expected_improvement_pct: int = 10
# Expected number of test files discovered
expected_test_files: Optional[int] = None
def clear_codeflash_directory(cwd: pathlib.Path) -> None:
"""Clear the .codeflash directory to avoid stale state."""
codeflash_dir = cwd / ".codeflash"
if codeflash_dir.exists():
shutil.rmtree(codeflash_dir)
def install_npm_dependencies(cwd: pathlib.Path) -> bool:
"""Install npm dependencies if needed."""
node_modules = cwd / "node_modules"
if not node_modules.exists():
logging.info(f"Installing npm dependencies in {cwd}")
result = subprocess.run(
["npm", "install"],
cwd=str(cwd),
capture_output=True,
text=True,
)
if result.returncode != 0:
logging.error(f"npm install failed: {result.stderr}")
return False
return True
def build_js_command(
cwd: pathlib.Path,
config: JSTestConfig,
) -> list[str]:
"""Build the codeflash CLI command for JS/TS optimization."""
# Use relative path for python to find the main module
python_path = "../../codeflash/main.py"
if "code_to_optimize_js" in str(cwd):
python_path = "../../../../codeflash/main.py"
base_command = [
"uv",
"run",
"--no-project",
python_path,
"--file",
str(config.file_path),
"--no-pr",
]
if config.function_name:
base_command.extend(["--function", config.function_name])
return base_command
def validate_js_output(
stdout: str,
return_code: int,
config: JSTestConfig,
) -> bool:
"""Validate the output of a JS/TS optimization run."""
if return_code != 0:
logging.error(f"Command returned exit code {return_code} instead of 0")
return False
if "⚡️ Optimization successful! 📄 " not in stdout:
logging.error("Failed to find performance improvement message")
return False
improvement_match = re.search(r"📈 ([\d,]+)% (?:(\w+) )?improvement", stdout)
if not improvement_match:
logging.error("Could not find improvement percentage in output")
return False
improvement_pct = int(improvement_match.group(1).replace(",", ""))
improvement_x = float(improvement_pct) / 100
logging.info(f"Performance improvement: {improvement_pct}%; Rate: {improvement_x}x")
if improvement_pct <= config.expected_improvement_pct:
logging.error(
f"Performance improvement {improvement_pct}% not above {config.expected_improvement_pct}%"
)
return False
if improvement_x <= config.min_improvement_x:
logging.error(
f"Performance improvement rate {improvement_x}x not above {config.min_improvement_x}x"
)
return False
if config.expected_test_files is not None:
test_files_match = re.search(r"Discovered (\d+) existing unit test files?", stdout)
if not test_files_match:
logging.error("Could not find unit test file count")
return False
num_test_files = int(test_files_match.group(1))
if num_test_files != config.expected_test_files:
logging.error(
f"Expected {config.expected_test_files} test files, found {num_test_files}"
)
return False
logging.info(f"Success: Performance improvement is {improvement_pct}%")
return True
def run_js_codeflash_command(
cwd: pathlib.Path,
config: JSTestConfig,
) -> bool:
"""Run codeflash optimization on a JavaScript/TypeScript project."""
logging.basicConfig(level=logging.INFO)
# Save original file contents for potential revert
path_to_file = cwd / config.file_path
file_contents = path_to_file.read_text("utf-8")
# Clear any stale state
clear_codeflash_directory(cwd)
# Install dependencies if needed
if not install_npm_dependencies(cwd):
return False
# Build and run command
command = build_js_command(cwd, config)
env = os.environ.copy()
env["PYTHONIOENCODING"] = "utf-8"
logging.info(f"Running: {' '.join(command)}")
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
cwd=str(cwd),
env=env,
encoding="utf-8",
)
output = []
for line in process.stdout:
logging.info(line.strip())
output.append(line)
return_code = process.wait()
stdout = "".join(output)
validated = validate_js_output(stdout, return_code, config)
if not validated:
# Revert file changes on failure
path_to_file.write_text(file_contents, "utf-8")
logging.info("Codeflash run did not meet expected requirements, reverting file changes.")
return False
return validated
def run_with_retries(test_func, *args, **kwargs) -> int:
"""Run a test function with retries on failure."""
max_retries = int(os.getenv("MAX_RETRIES", 3))
retry_delay = int(os.getenv("RETRY_DELAY", 5))
log = logging.getLogger()
log.setLevel(logging.DEBUG)
for attempt in range(1, max_retries + 1):
logging.info(f"\n=== Attempt {attempt} of {max_retries} ===")
if test_func(*args, **kwargs):
logging.info(f"Test passed on attempt {attempt}")
return 0
logging.error(f"Test failed on attempt {attempt}")
if attempt < max_retries:
logging.info(f"Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
else:
logging.error("Test failed after all retries")
return 1
return 1

View file

@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""Runner script for all JavaScript/TypeScript end-to-end tests.
This script runs all JS/TS e2e tests and reports results.
Usage:
python run_js_e2e_tests.py [--test TEST_NAME] [--parallel]
Examples:
python run_js_e2e_tests.py # Run all tests sequentially
python run_js_e2e_tests.py --test fibonacci # Run only fibonacci tests
python run_js_e2e_tests.py --parallel # Run tests in parallel
"""
import argparse
import subprocess
import sys
import time
from concurrent.futures import ProcessPoolExecutor, as_completed
from pathlib import Path
from typing import NamedTuple
class TestResult(NamedTuple):
name: str
success: bool
duration: float
output: str
# List of all JS/TS e2e tests - one per module type, each testing different code patterns
JS_E2E_TESTS = [
# CommonJS - Simple function optimization (recursive fibonacci)
"end_to_end_test_js_cjs_function.py",
# TypeScript - Class method optimization (DataProcessor.findDuplicates)
"end_to_end_test_js_ts_class.py",
# ES Modules - Async function optimization (processItemsSequential)
"end_to_end_test_js_esm_async.py",
]
def run_single_test(test_file: str) -> TestResult:
"""Run a single test and return the result."""
script_dir = Path(__file__).parent
test_path = script_dir / test_file
start_time = time.time()
try:
result = subprocess.run(
["python", str(test_path)],
capture_output=True,
text=True,
timeout=600, # 10 minute timeout
cwd=str(script_dir),
)
success = result.returncode == 0
output = result.stdout + result.stderr
except subprocess.TimeoutExpired:
success = False
output = "Test timed out after 600 seconds"
except Exception as e:
success = False
output = f"Error running test: {e}"
duration = time.time() - start_time
return TestResult(
name=test_file.replace(".py", ""),
success=success,
duration=duration,
output=output,
)
def run_tests_sequential(tests: list[str]) -> list[TestResult]:
"""Run tests sequentially."""
results = []
for test in tests:
print(f"\n{'='*60}")
print(f"Running: {test}")
print("=" * 60)
result = run_single_test(test)
results.append(result)
status = "✅ PASSED" if result.success else "❌ FAILED"
print(f"{status} in {result.duration:.1f}s")
if not result.success:
print(f"Output:\n{result.output}")
return results
def run_tests_parallel(tests: list[str], max_workers: int = 4) -> list[TestResult]:
"""Run tests in parallel."""
results = []
with ProcessPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(run_single_test, test): test for test in tests}
for future in as_completed(futures):
test = futures[future]
result = future.result()
results.append(result)
status = "✅ PASSED" if result.success else "❌ FAILED"
print(f"{status}: {result.name} ({result.duration:.1f}s)")
return results
def print_summary(results: list[TestResult]) -> None:
"""Print a summary of test results."""
print("\n" + "=" * 60)
print("TEST SUMMARY")
print("=" * 60)
passed = [r for r in results if r.success]
failed = [r for r in results if not r.success]
print(f"\nTotal: {len(results)}")
print(f"Passed: {len(passed)}")
print(f"Failed: {len(failed)}")
if failed:
print("\nFailed tests:")
for r in failed:
print(f"{r.name}")
total_duration = sum(r.duration for r in results)
print(f"\nTotal duration: {total_duration:.1f}s")
def main() -> int:
parser = argparse.ArgumentParser(description="Run JS/TS e2e tests")
parser.add_argument(
"--test",
type=str,
help="Run only tests matching this pattern",
)
parser.add_argument(
"--parallel",
action="store_true",
help="Run tests in parallel",
)
parser.add_argument(
"--workers",
type=int,
default=4,
help="Number of parallel workers (default: 4)",
)
args = parser.parse_args()
# Filter tests if pattern specified
tests = JS_E2E_TESTS
if args.test:
tests = [t for t in tests if args.test.lower() in t.lower()]
if not tests:
print(f"No tests matching pattern: {args.test}")
return 1
print(f"Running {len(tests)} test(s)...")
# Run tests
if args.parallel:
results = run_tests_parallel(tests, args.workers)
else:
results = run_tests_sequential(tests)
# Print summary
print_summary(results)
# Return exit code
failed = [r for r in results if not r.success]
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main())