Skip to content

Commit aa79b88

Browse files
author
DavertMik
committed
started ESM conversion
1 parent f7ff2bf commit aa79b88

27 files changed

+827
-228
lines changed

.cursor/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# CodeceptJS MCP Server for Cursor IDE
2+
3+
This project includes a Model Context Protocol (MCP) server for integration with Cursor IDE. The MCP server provides context-aware assistance for working with CodeceptJS tests.
4+
5+
## Available Resources
6+
7+
When using Cursor with this project, you'll have access to the following resources:
8+
9+
- **CodeceptJS Tests** - Complete list of tests with file locations and test bodies
10+
- **Test Suites** - Organized view of test suites and their tests
11+
- **Available Actions** - List of all helper and actor (I) actions that can be used in tests
12+
13+
## How to Use
14+
15+
1. Open this project in Cursor IDE
16+
2. The MCP server will automatically start when needed
17+
3. When writing or editing tests, you can reference:
18+
- Existing tests using `/tests`
19+
- Test suites using `/suites`
20+
- Available actions using `/actions`
21+
22+
## Example
23+
24+
When writing a test, you can ask Cursor about available actions:
25+
26+
```
27+
Can you show me what actions are available for filling out forms?
28+
```
29+
30+
Or inquire about existing tests:
31+
32+
```
33+
Show me the login tests in this project
34+
```
35+
36+
## Troubleshooting
37+
38+
If the MCP server isn't connecting:
39+
40+
1. Make sure all dependencies are installed with `npm install`
41+
2. Try restarting Cursor
42+
3. Check that the `codeceptjs mcp` command works from the terminal

.cursor/mcp.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"mcpServers": {
3+
"codeceptjs": {
4+
"command": "node",
5+
"args": ["/home/davert/projects/CodeceptJS/bin/codecept-mcp.js", "/home/davert/projects/CodeceptJS/examples"],
6+
"cwd": "/home/davert/projects/CodeceptJS/examples"
7+
}
8+
}
9+
}

CLAUDE.md

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# CodeceptJS ESM Migration Plan
2+
3+
## Executive Summary
4+
5+
Migrating CodeceptJS to ESM will require a phased approach due to:
6+
7+
- **900+ require() statements** across the codebase
8+
- Complex dynamic module loading system in `container.js`
9+
- Extensive plugin architecture with runtime module resolution
10+
- Heavy use of conditional requires and try-catch patterns
11+
12+
## Analysis Results
13+
14+
### Total Count of require() Statements
15+
16+
- **850 require() statements** found in the `/lib` directory alone
17+
- Additional requires in `/bin`, `/translations`, and root files
18+
- **Total estimated across codebase: ~900+ require() statements**
19+
20+
### Files with Most require() Statements
21+
22+
1. `/lib/codecept.js` - 30 requires
23+
2. `/lib/helper/Playwright.js` - 28 requires
24+
3. `/lib/helper/Puppeteer.js` - 26 requires
25+
4. `/lib/helper/WebDriver.js` - 24 requires
26+
5. `/lib/helper/Protractor.js` - 22 requires
27+
6. `/lib/workers.js` - 20 requires
28+
7. `/lib/index.js` - 19 requires
29+
8. `/lib/container.js` - 19 requires
30+
9. `/lib/command/init.js` - 19 requires
31+
10. `/lib/helper/TestCafe.js` - 17 requires
32+
33+
### Problematic Patterns Requiring Special Handling
34+
35+
#### 1. Dynamic require() Statements (Most Critical)
36+
37+
- **Template-based requires**: `/lib/command/generate.js` line 158: `actor = require('${actorPath}')`
38+
- **Conditional module loading**: `/lib/command/init.js` line 287: `require(\`../helper/\${helperName}\`)`
39+
- **Plugin loading**: Multiple files use `require(module)` where module is a variable
40+
41+
#### 2. Conditional require() Statements (High Impact)
42+
43+
Found **75+ files** with conditional requires using `if` statements:
44+
45+
- `/lib/container.js`: Module loading with fallback logic
46+
- `/lib/config.js`: TypeScript support with `require('ts-node/register')`
47+
- `/lib/codecept.js`: Global object loading based on configuration
48+
- Helper files: Conditional loading of browser automation libraries
49+
50+
#### 3. Try-catch require() Patterns (High Impact)
51+
52+
Found **57+ files** with try-catch require patterns:
53+
54+
- `/lib/utils.js`: `requireWithFallback()` function for graceful fallbacks
55+
- `/lib/plugin/wdio.js`: `safeRequire()` function
56+
- `/lib/helper/REST.js`: Dependency checking
57+
- `/lib/container.js`: Module loading with error handling
58+
59+
#### 4. require.resolve() Usage (Medium Impact)
60+
61+
Found **5 files** using `require.resolve()`:
62+
63+
- `/lib/utils.js`: Package existence checking in `requireWithFallback()`
64+
- `/lib/helper/Puppeteer.js`: Module resolution
65+
- `/lib/helper/extras/React.js`: Path resolution
66+
- `/lib/helper/ApiDataFactory.js` and `/lib/helper/GraphQLDataFactory.js`: Dependency checking
67+
68+
#### 5. \_\_dirname Usage (Medium Impact)
69+
70+
Found **9 files** using `__dirname`:
71+
72+
- `/lib/codecept.js`: Package.json path resolution
73+
- `/lib/utils.js`: Local installation detection
74+
- `/lib/workers.js`: Worker script path resolution
75+
- `/lib/command/generate.js`: Template file paths
76+
- `/lib/command/run-multiple.js`: Executable paths
77+
- `/lib/helper/Nightmare.js`: Client script injection
78+
- `/lib/helper/REST.js`: Certificate file paths (in documentation)
79+
- `/lib/mocha/factory.js`: UI module paths
80+
- `/lib/helper/testcafe/testcafe-utils.js`: Directory resolution
81+
82+
## Migration Plan
83+
84+
### Phase 1: Foundation (Estimated 2-3 weeks)
85+
86+
#### 1.1 Update package.json
87+
88+
```json
89+
{
90+
"type": "module",
91+
"exports": {
92+
".": "./lib/index.js",
93+
"./lib/*": "./lib/*.js"
94+
},
95+
"engines": {
96+
"node": ">=14.13.1"
97+
}
98+
}
99+
```
100+
101+
#### 1.2 Create ESM compatibility layer
102+
103+
- Create `lib/compat/moduleLoader.js` for dynamic imports
104+
- Convert `__dirname`/`__filename` to `import.meta.url`
105+
- Replace `require.resolve()` with `import.meta.resolve()`
106+
107+
#### 1.3 Convert bin/ entry points
108+
109+
- `bin/codecept.js` → ESM imports
110+
- Update shebang and command structure
111+
112+
### Phase 2: Core Infrastructure (Estimated 3-4 weeks)
113+
114+
#### 2.1 Convert container.js (CRITICAL)
115+
116+
Key changes needed in `lib/container.js:285-305`:
117+
118+
```javascript
119+
// Current problematic code:
120+
const mod = require(moduleName)
121+
HelperClass = mod.default || mod
122+
123+
// ESM replacement:
124+
const mod = await import(moduleName)
125+
HelperClass = mod.default || mod
126+
```
127+
128+
#### 2.2 Update dynamic loading functions
129+
130+
- `requireHelperFromModule()``importHelperFromModule()`
131+
- `loadSupportObject()` → async with dynamic imports
132+
- `createPlugins()` → async plugin loading
133+
134+
#### 2.3 Convert core files
135+
136+
Priority order:
137+
138+
1. `lib/utils.js` (19 requires)
139+
2. `lib/index.js` (19 requires)
140+
3. `lib/codecept.js` (30 requires)
141+
4. `lib/config.js` (13 requires)
142+
143+
### Phase 3: Helper System (Estimated 4-5 weeks)
144+
145+
#### 3.1 Convert browser automation helpers
146+
147+
High-impact files:
148+
149+
- `lib/helper/Playwright.js` (28 requires)
150+
- `lib/helper/Puppeteer.js` (26 requires)
151+
- `lib/helper/WebDriver.js` (24 requires)
152+
- `lib/helper/TestCafe.js` (17 requires)
153+
154+
#### 3.2 Handle conditional dependencies
155+
156+
Convert try-catch patterns:
157+
158+
```javascript
159+
// Current:
160+
try {
161+
const puppeteer = require('puppeteer')
162+
} catch (e) {
163+
// fallback
164+
}
165+
166+
// ESM:
167+
let puppeteer
168+
try {
169+
puppeteer = await import('puppeteer')
170+
} catch (e) {
171+
// fallback
172+
}
173+
```
174+
175+
### Phase 4: Commands & Plugins (Estimated 2-3 weeks)
176+
177+
#### 4.1 Convert command files
178+
179+
- `lib/command/init.js` (19 requires)
180+
- `lib/command/generate.js` (template loading)
181+
- `lib/command/run*.js` files
182+
183+
#### 4.2 Update plugin system
184+
185+
- Support both CJS and ESM plugins
186+
- Async plugin initialization
187+
- Plugin discovery mechanism
188+
189+
### Phase 5: Testing & Validation (Estimated 2-3 weeks)
190+
191+
#### 5.1 Test compatibility
192+
193+
- All existing tests must pass
194+
- Plugin ecosystem compatibility
195+
- Helper loading in various configurations
196+
197+
#### 5.2 Documentation updates
198+
199+
- Migration guide for users
200+
- Plugin development guidelines
201+
- Breaking changes documentation
202+
203+
## Critical Migration Challenges
204+
205+
### 1. Dynamic Module Loading
206+
207+
The biggest challenge is in `container.js` where modules are loaded dynamically based on configuration. This requires converting synchronous `require()` to asynchronous `import()`.
208+
209+
**Solution**: Make container creation async and update all callers.
210+
211+
### 2. Plugin Ecosystem
212+
213+
Many plugins may still use CommonJS.
214+
215+
**Solution**: Support both formats during transition period.
216+
217+
### 3. Global Object Injection
218+
219+
`codecept.js` conditionally adds globals based on config.
220+
221+
**Solution**: Maintain compatibility layer for existing configurations.
222+
223+
### 4. File Path Resolution
224+
225+
Extensive use of `__dirname` for path resolution.
226+
227+
**Solution**: Create utility functions using `import.meta.url`.
228+
229+
## Compatibility Strategy
230+
231+
### Dual Package Approach (Recommended)
232+
233+
1. Maintain CommonJS build for compatibility
234+
2. Provide ESM build for modern usage
235+
3. Use `package.json` exports field to serve appropriate version
236+
237+
### Breaking Changes
238+
239+
- Drop Node.js < 14.13.1 support
240+
- Async container initialization
241+
- Some plugin APIs may need updates
242+
243+
## Timeline Summary
244+
245+
- **Total estimated time**: 13-18 weeks
246+
- **Critical path**: Container.js and dynamic loading
247+
- **Risk areas**: Plugin compatibility, helper loading
248+
249+
## Next Steps
250+
251+
1. Start with Phase 1 foundation work
252+
2. Create comprehensive test suite for migration validation
253+
3. Engage with plugin maintainers early
254+
4. Consider feature freeze during migration
255+
5. Plan gradual rollout strategy
256+
257+
## Testing Commands
258+
259+
To test the project:
260+
261+
- `npm run lint` - Run linting
262+
- `npm run test:unit` - Run unit tests
263+
- `npm run test:runner` - Run runner tests
264+
- `npm run test` - Run both unit and runner tests
265+
266+
## Current Migration Status
267+
268+
Starting with creating example-esm project for iterative testing and validation.

example-esm/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
output/
3+
package-lock.json

example-esm/basic_test.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
Feature('Basic ESM Tests')
2+
3+
Scenario('Test basic assertions', ({ I }) => {
4+
I.log('Starting basic assertion tests')
5+
6+
// Test equality
7+
I.assertEqual(1 + 1, 2, 'Math should work')
8+
I.assertNotEqual('hello', 'world', 'Strings should be different')
9+
10+
// Test boolean values
11+
I.assertTrue(true, 'True should be true')
12+
I.assertFalse(false, 'False should be false')
13+
14+
// Test existence
15+
I.assertExists('not empty', 'String should exist')
16+
I.assertNotExists(null, 'Null should not exist')
17+
18+
// Test arrays and strings
19+
const testArray = [1, 2, 3, 'test']
20+
I.assertContains(testArray, 'test', 'Array should contain test string')
21+
I.assertNotContains(testArray, 'missing', 'Array should not contain missing item')
22+
23+
const testString = 'Hello CodeceptJS ESM World'
24+
I.assertContains(testString, 'ESM', 'String should contain ESM')
25+
I.assertNotContains(testString, 'CJS', 'String should not contain CJS')
26+
27+
// Test numbers
28+
I.assertGreaterThan(10, 5, '10 should be greater than 5')
29+
I.assertLessThan(3, 7, '3 should be less than 7')
30+
31+
I.log('All basic assertions passed!')
32+
})
33+
34+
Scenario('Test utility functions', async ({ I }) => {
35+
I.log('Testing utility functions')
36+
37+
// Test timestamp - helper methods return promises in CodeceptJS
38+
const timestamp = await I.getCurrentTimestamp()
39+
I.assertExists(timestamp, 'Timestamp should exist')
40+
I.assertTrue(timestamp.includes('T'), 'Timestamp should be in ISO format')
41+
42+
// Test random string generation
43+
const randomStr1 = await I.generateRandomString(5)
44+
const randomStr2 = await I.generateRandomString(5)
45+
I.assertEqual(randomStr1.length, 5, 'Random string should be 5 chars')
46+
I.assertNotEqual(randomStr1, randomStr2, 'Random strings should be different')
47+
48+
I.log('Utility function tests passed!')
49+
})
50+
51+
Scenario('Test error handling', ({ I }) => {
52+
I.log('Testing error handling')
53+
54+
// Test function that throws
55+
I.assertThrows(() => {
56+
throw new Error('Test error')
57+
}, 'Function should throw error')
58+
59+
// Test function that doesn't throw
60+
I.assertDoesNotThrow(() => {
61+
return 'no error'
62+
}, 'Function should not throw error')
63+
64+
I.log('Error handling tests passed!')
65+
})

0 commit comments

Comments
 (0)