chrometools-mcp 3.5.4 → 3.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/COMPONENT_MAPPING_SPEC.md +1217 -1217
- package/README.md +13 -4
- package/SPEC-swagger-api-tools.md +3101 -3101
- package/browser/page-manager.js +7 -0
- package/index.js +199 -14
- package/models/DATEPICKER_IMPLEMENTATION.md +543 -543
- package/models/ModelRegistry.js +115 -115
- package/package.json +1 -1
- package/pom/apom-tree-converter.js +56 -1
- package/server/tool-definitions.js +10 -5
- package/server/tool-schemas.js +10 -5
- package/specs/SEGM-537-UNBLOCKERS_PROGRESS.md +94 -0
- package/specs/SEGM-537-UNBLOCKERS_SPEC.md +187 -0
- package/utils/actions/click-action.js +76 -8
- package/SPEC-IMPROVEMENTS.md +0 -173
- package/SPEC-pom-integration.md +0 -227
|
@@ -1,543 +1,543 @@
|
|
|
1
|
-
# DatePicker Models Implementation Plan
|
|
2
|
-
|
|
3
|
-
**Status**: Architecture approved, implementation pending
|
|
4
|
-
**Date**: 2026-02-15
|
|
5
|
-
**Decision**: Separate models for each UI framework (approved)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Architecture Decision
|
|
10
|
-
|
|
11
|
-
### ✅ APPROVED: Separate Models for Each Framework
|
|
12
|
-
|
|
13
|
-
**Rationale**:
|
|
14
|
-
- DatePickers are fundamentally different across frameworks (input-based vs popup vs custom render)
|
|
15
|
-
- Impossible to create universal logic without brittle code
|
|
16
|
-
- Each framework has specific interactions and edge cases
|
|
17
|
-
- Easier to maintain, test, and extend
|
|
18
|
-
- API remains unified for AI - `executeModelAction` works the same way
|
|
19
|
-
|
|
20
|
-
**Structure**:
|
|
21
|
-
```
|
|
22
|
-
models/
|
|
23
|
-
├── mui-datepicker-model.js → MuiDatePickerModel
|
|
24
|
-
├── ant-datepicker-model.js → AntDatePickerModel
|
|
25
|
-
├── react-datepicker-model.js → ReactDatePickerModel
|
|
26
|
-
└── vuetify-datepicker-model.js → VuetifyDatePickerModel (future)
|
|
27
|
-
|
|
28
|
-
utils/actions/
|
|
29
|
-
├── mui-datepicker-action.js → executeMuiDatePickerAction
|
|
30
|
-
├── ant-datepicker-action.js → executeAntDatePickerAction
|
|
31
|
-
└── react-datepicker-action.js → executeReactDatePickerAction
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Current Status
|
|
37
|
-
|
|
38
|
-
### ✅ Implemented
|
|
39
|
-
|
|
40
|
-
1. **Model System with Strategy Pattern**
|
|
41
|
-
- ✅ `models/ElementModel.js` - base class
|
|
42
|
-
- ✅ `models/index.js` - 13 concrete models
|
|
43
|
-
- ✅ `models/ModelRegistry.js` - registry with findModel()
|
|
44
|
-
- ✅ Integration in `apom-tree-converter.js`
|
|
45
|
-
|
|
46
|
-
2. **executeModelAction Tool**
|
|
47
|
-
- ✅ Supports APOM ID (`id` parameter)
|
|
48
|
-
- ✅ Supports CSS selector (`selector` parameter)
|
|
49
|
-
- ✅ Routing via ModelRegistry.getActionHandler()
|
|
50
|
-
- ✅ Action parameters via `params`
|
|
51
|
-
|
|
52
|
-
3. **Action Handlers (Reusable)**
|
|
53
|
-
- ✅ `executeClickAction` - used by all models
|
|
54
|
-
- ✅ `executeTypeAction` - TxtInp, DateInp, Range, Color
|
|
55
|
-
- ✅ `executeHoverAction` - all models
|
|
56
|
-
- ✅ `executeScreenshotAction` - all models
|
|
57
|
-
- ✅ `executeSelectOptionAction` - Sel (select)
|
|
58
|
-
- ✅ `executeCheckAction` - Chk, Radio (check/uncheck/toggle)
|
|
59
|
-
|
|
60
|
-
4. **APOM Tree Integration**
|
|
61
|
-
- ✅ Models appear in `analyzePage` with model name
|
|
62
|
-
- ✅ `models` map shows available actions per model
|
|
63
|
-
- ✅ Checkboxes with `opacity:0` are now visible (fixed `isVisible()`)
|
|
64
|
-
|
|
65
|
-
5. **Material UI DatePicker Detection** (Session Fix)
|
|
66
|
-
- ✅ Updated `DatePickerModel.matches()` to detect MUI DatePicker
|
|
67
|
-
- ✅ Detection logic:
|
|
68
|
-
```javascript
|
|
69
|
-
if (classes.includes('MuiFormControl') || classes.includes('MuiTextField')) {
|
|
70
|
-
const hasInput = element.querySelector('input[type="text"]');
|
|
71
|
-
const hasCalendarIcon = element.querySelector('button[aria-label*="date"]');
|
|
72
|
-
return hasInput && hasCalendarIcon;
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## ❌ NOT Implemented (TODO)
|
|
79
|
-
|
|
80
|
-
### 1. Material UI DatePicker Model & Action
|
|
81
|
-
|
|
82
|
-
**File**: `models/mui-datepicker-model.js`
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
class MuiDatePickerModel extends ElementModel {
|
|
86
|
-
getName() {
|
|
87
|
-
return 'MuiDatePicker';
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
getActions() {
|
|
91
|
-
return ['SetDate', 'SetDateTime', 'SetTime', 'click', 'clear'];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
getPriority() {
|
|
95
|
-
return 100; // Check before generic DatePickerModel
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
matches(element, elementType) {
|
|
99
|
-
const classes = element.className || '';
|
|
100
|
-
|
|
101
|
-
// MUI v5/v6 DatePicker: MuiFormControl/MuiTextField + input + calendar button
|
|
102
|
-
if (classes.includes('MuiFormControl') || classes.includes('MuiTextField')) {
|
|
103
|
-
const hasInput = element.querySelector('input[type="text"]');
|
|
104
|
-
const hasCalendarIcon = element.querySelector('button[aria-label*="date" i], button[aria-label*="calendar" i]');
|
|
105
|
-
return hasInput && hasCalendarIcon;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
getActionHandler(actionName) {
|
|
112
|
-
const handlers = {
|
|
113
|
-
'SetDate': 'executeMuiDatePickerAction',
|
|
114
|
-
'SetDateTime': 'executeMuiDatePickerAction',
|
|
115
|
-
'SetTime': 'executeMuiDatePickerAction',
|
|
116
|
-
'click': 'executeClickAction',
|
|
117
|
-
'clear': 'executeMuiDatePickerAction'
|
|
118
|
-
};
|
|
119
|
-
return handlers[actionName] || null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
**File**: `utils/actions/mui-datepicker-action.js`
|
|
125
|
-
|
|
126
|
-
```javascript
|
|
127
|
-
/**
|
|
128
|
-
* Material UI DatePicker Action Handler
|
|
129
|
-
* Handles SetDate, SetDateTime, SetTime, clear actions for MUI DatePicker
|
|
130
|
-
*/
|
|
131
|
-
|
|
132
|
-
export async function executeMuiDatePickerAction(page, element, action, params) {
|
|
133
|
-
const identifier = params.identifier || 'MUI DatePicker';
|
|
134
|
-
|
|
135
|
-
if (action === 'SetDate' || action === 'SetDateTime') {
|
|
136
|
-
// Find input inside MUI DatePicker
|
|
137
|
-
const input = await element.$('input[type="text"]');
|
|
138
|
-
if (!input) {
|
|
139
|
-
throw new Error(`Input not found inside ${identifier}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Strategy 1: Direct input.value set (works for MUI v5/v6)
|
|
143
|
-
await input.evaluate((el, date) => {
|
|
144
|
-
el.value = date;
|
|
145
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
146
|
-
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
147
|
-
}, params.date || params.datetime);
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
content: [
|
|
151
|
-
{ type: "text", text: `Set ${action} to "${params.date || params.datetime}" in ${identifier}` }
|
|
152
|
-
]
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (action === 'SetTime') {
|
|
157
|
-
// MUI TimePicker logic (similar to SetDate)
|
|
158
|
-
const input = await element.$('input[type="text"]');
|
|
159
|
-
await input.evaluate((el, time) => {
|
|
160
|
-
el.value = time;
|
|
161
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
162
|
-
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
163
|
-
}, params.time);
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
content: [
|
|
167
|
-
{ type: "text", text: `Set time to "${params.time}" in ${identifier}` }
|
|
168
|
-
]
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (action === 'clear') {
|
|
173
|
-
const input = await element.$('input[type="text"]');
|
|
174
|
-
await input.evaluate(el => {
|
|
175
|
-
el.value = '';
|
|
176
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
177
|
-
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
content: [
|
|
182
|
-
{ type: "text", text: `Cleared ${identifier}` }
|
|
183
|
-
]
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
throw new Error(`Unknown action: ${action}`);
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
**Registration**:
|
|
192
|
-
1. Add to `models/index.js` MODELS array
|
|
193
|
-
2. Add to `utils/actions/index.js` exports
|
|
194
|
-
3. Add to `index.js` imports and actionHandlers map
|
|
195
|
-
4. Add invocation logic in executeModelAction handler
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
### 2. Ant Design DatePicker Model & Action
|
|
200
|
-
|
|
201
|
-
**File**: `models/ant-datepicker-model.js`
|
|
202
|
-
|
|
203
|
-
```javascript
|
|
204
|
-
class AntDatePickerModel extends ElementModel {
|
|
205
|
-
getName() {
|
|
206
|
-
return 'AntDatePicker';
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
getActions() {
|
|
210
|
-
return ['SetDate', 'SetRange', 'SetDateTime', 'click', 'clear'];
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
getPriority() {
|
|
214
|
-
return 100;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
matches(element, elementType) {
|
|
218
|
-
const classes = element.className || '';
|
|
219
|
-
return classes.includes('ant-picker');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
getActionHandler(actionName) {
|
|
223
|
-
const handlers = {
|
|
224
|
-
'SetDate': 'executeAntDatePickerAction',
|
|
225
|
-
'SetRange': 'executeAntDatePickerAction',
|
|
226
|
-
'SetDateTime': 'executeAntDatePickerAction',
|
|
227
|
-
'click': 'executeClickAction',
|
|
228
|
-
'clear': 'executeAntDatePickerAction'
|
|
229
|
-
};
|
|
230
|
-
return handlers[actionName] || null;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
**File**: `utils/actions/ant-datepicker-action.js`
|
|
236
|
-
|
|
237
|
-
```javascript
|
|
238
|
-
/**
|
|
239
|
-
* Ant Design DatePicker Action Handler
|
|
240
|
-
* Handles SetDate, SetRange, SetDateTime, clear actions for Ant Design DatePicker
|
|
241
|
-
*
|
|
242
|
-
* Note: Ant DatePicker uses popup calendar, requires complex interaction
|
|
243
|
-
*/
|
|
244
|
-
|
|
245
|
-
export async function executeAntDatePickerAction(page, element, action, params) {
|
|
246
|
-
const identifier = params.identifier || 'Ant DatePicker';
|
|
247
|
-
|
|
248
|
-
if (action === 'SetDate' || action === 'SetDateTime') {
|
|
249
|
-
// Strategy 1: Try direct input value set (works for some Ant versions)
|
|
250
|
-
const input = await element.$('input');
|
|
251
|
-
if (input) {
|
|
252
|
-
try {
|
|
253
|
-
await input.evaluate((el, date) => {
|
|
254
|
-
el.value = date;
|
|
255
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
256
|
-
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
257
|
-
}, params.date || params.datetime);
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
content: [
|
|
261
|
-
{ type: "text", text: `Set ${action} to "${params.date || params.datetime}" in ${identifier}` }
|
|
262
|
-
]
|
|
263
|
-
};
|
|
264
|
-
} catch (e) {
|
|
265
|
-
// Fallback to popup interaction
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Strategy 2: Popup interaction (more reliable but complex)
|
|
270
|
-
// 1. Click to open popup
|
|
271
|
-
await element.click();
|
|
272
|
-
await page.waitForSelector('.ant-picker-dropdown', { timeout: 2000 });
|
|
273
|
-
|
|
274
|
-
// 2. Parse date and navigate calendar
|
|
275
|
-
// ... (complex calendar interaction logic)
|
|
276
|
-
|
|
277
|
-
throw new Error('Popup interaction not implemented yet - use direct input strategy');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (action === 'SetRange') {
|
|
281
|
-
// Range picker specific logic (two dates)
|
|
282
|
-
throw new Error('SetRange action not implemented yet');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (action === 'clear') {
|
|
286
|
-
const clearBtn = await element.$('.ant-picker-clear');
|
|
287
|
-
if (clearBtn) {
|
|
288
|
-
await clearBtn.click();
|
|
289
|
-
} else {
|
|
290
|
-
const input = await element.$('input');
|
|
291
|
-
await input.evaluate(el => {
|
|
292
|
-
el.value = '';
|
|
293
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return {
|
|
298
|
-
content: [
|
|
299
|
-
{ type: "text", text: `Cleared ${identifier}` }
|
|
300
|
-
]
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
throw new Error(`Unknown action: ${action}`);
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
|
|
310
|
-
### 3. react-datepicker Model & Action
|
|
311
|
-
|
|
312
|
-
**File**: `models/react-datepicker-model.js`
|
|
313
|
-
|
|
314
|
-
```javascript
|
|
315
|
-
class ReactDatePickerModel extends ElementModel {
|
|
316
|
-
getName() {
|
|
317
|
-
return 'ReactDatePicker';
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
getActions() {
|
|
321
|
-
return ['SetDate', 'SetDateTime', 'click', 'clear'];
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
getPriority() {
|
|
325
|
-
return 100;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
matches(element, elementType) {
|
|
329
|
-
const classes = element.className || '';
|
|
330
|
-
return classes.includes('react-datepicker-wrapper') ||
|
|
331
|
-
classes.includes('react-datepicker__input-container');
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
getActionHandler(actionName) {
|
|
335
|
-
const handlers = {
|
|
336
|
-
'SetDate': 'executeReactDatePickerAction',
|
|
337
|
-
'SetDateTime': 'executeReactDatePickerAction',
|
|
338
|
-
'click': 'executeClickAction',
|
|
339
|
-
'clear': 'executeReactDatePickerAction'
|
|
340
|
-
};
|
|
341
|
-
return handlers[actionName] || null;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
**File**: `utils/actions/react-datepicker-action.js`
|
|
347
|
-
|
|
348
|
-
```javascript
|
|
349
|
-
/**
|
|
350
|
-
* react-datepicker Action Handler
|
|
351
|
-
*/
|
|
352
|
-
|
|
353
|
-
export async function executeReactDatePickerAction(page, element, action, params) {
|
|
354
|
-
const identifier = params.identifier || 'React DatePicker';
|
|
355
|
-
|
|
356
|
-
if (action === 'SetDate' || action === 'SetDateTime') {
|
|
357
|
-
const input = await element.$('input');
|
|
358
|
-
|
|
359
|
-
await input.evaluate((el, date) => {
|
|
360
|
-
el.value = date;
|
|
361
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
362
|
-
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
363
|
-
}, params.date || params.datetime);
|
|
364
|
-
|
|
365
|
-
return {
|
|
366
|
-
content: [
|
|
367
|
-
{ type: "text", text: `Set ${action} to "${params.date || params.datetime}" in ${identifier}` }
|
|
368
|
-
]
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (action === 'clear') {
|
|
373
|
-
const input = await element.$('input');
|
|
374
|
-
await input.evaluate(el => {
|
|
375
|
-
el.value = '';
|
|
376
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
return {
|
|
380
|
-
content: [
|
|
381
|
-
{ type: "text", text: `Cleared ${identifier}` }
|
|
382
|
-
]
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
throw new Error(`Unknown action: ${action}`);
|
|
387
|
-
}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
---
|
|
391
|
-
|
|
392
|
-
### 4. Update models/index.js
|
|
393
|
-
|
|
394
|
-
Remove generic DatePickerModel, add specific models:
|
|
395
|
-
|
|
396
|
-
```javascript
|
|
397
|
-
// Remove or deprecate:
|
|
398
|
-
// class DatePickerModel extends ElementModel { ... }
|
|
399
|
-
|
|
400
|
-
// Add new imports at the top:
|
|
401
|
-
import { MuiDatePickerModel } from './mui-datepicker-model.js';
|
|
402
|
-
import { AntDatePickerModel } from './ant-datepicker-model.js';
|
|
403
|
-
import { ReactDatePickerModel } from './react-datepicker-model.js';
|
|
404
|
-
|
|
405
|
-
// Update MODELS array:
|
|
406
|
-
const MODELS = [
|
|
407
|
-
TextInputModel,
|
|
408
|
-
SelectModel,
|
|
409
|
-
ButtonModel,
|
|
410
|
-
CheckboxModel,
|
|
411
|
-
RadioModel,
|
|
412
|
-
TextAreaModel,
|
|
413
|
-
LinkModel,
|
|
414
|
-
RangeInputModel,
|
|
415
|
-
MuiDatePickerModel, // ← Add
|
|
416
|
-
AntDatePickerModel, // ← Add
|
|
417
|
-
ReactDatePickerModel, // ← Add
|
|
418
|
-
DateInputModel, // Native HTML5 date input
|
|
419
|
-
FileInputModel,
|
|
420
|
-
ColorInputModel,
|
|
421
|
-
DefaultModel
|
|
422
|
-
];
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
---
|
|
426
|
-
|
|
427
|
-
### 5. Update index.js
|
|
428
|
-
|
|
429
|
-
**Imports** (around line 73):
|
|
430
|
-
```javascript
|
|
431
|
-
import {
|
|
432
|
-
executeClickAction,
|
|
433
|
-
executeTypeAction,
|
|
434
|
-
executeHoverAction,
|
|
435
|
-
executeScreenshotAction,
|
|
436
|
-
executeSelectOptionAction,
|
|
437
|
-
executeCheckAction,
|
|
438
|
-
executeMuiDatePickerAction, // ← Add
|
|
439
|
-
executeAntDatePickerAction, // ← Add
|
|
440
|
-
executeReactDatePickerAction // ← Add
|
|
441
|
-
} from './utils/actions/index.js';
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
**actionHandlers map** (around line 633):
|
|
445
|
-
```javascript
|
|
446
|
-
const actionHandlers = {
|
|
447
|
-
'executeClickAction': executeClickAction,
|
|
448
|
-
'executeTypeAction': executeTypeAction,
|
|
449
|
-
'executeHoverAction': executeHoverAction,
|
|
450
|
-
'executeScreenshotAction': executeScreenshotAction,
|
|
451
|
-
'executeSelectOptionAction': executeSelectOptionAction,
|
|
452
|
-
'executeCheckAction': executeCheckAction,
|
|
453
|
-
'executeMuiDatePickerAction': executeMuiDatePickerAction, // ← Add
|
|
454
|
-
'executeAntDatePickerAction': executeAntDatePickerAction, // ← Add
|
|
455
|
-
'executeReactDatePickerAction': executeReactDatePickerAction // ← Add
|
|
456
|
-
};
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
**Invocation logic** (around line 653):
|
|
460
|
-
```javascript
|
|
461
|
-
// After executeCheckAction block
|
|
462
|
-
} else if (handlerInfo.handlerName === 'executeMuiDatePickerAction') {
|
|
463
|
-
result = await handlerFunction(page, element, validatedArgs.action, actionParams);
|
|
464
|
-
} else if (handlerInfo.handlerName === 'executeAntDatePickerAction') {
|
|
465
|
-
result = await handlerFunction(page, element, validatedArgs.action, actionParams);
|
|
466
|
-
} else if (handlerInfo.handlerName === 'executeReactDatePickerAction') {
|
|
467
|
-
result = await handlerFunction(page, element, validatedArgs.action, actionParams);
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
---
|
|
471
|
-
|
|
472
|
-
## Implementation Order (Recommended)
|
|
473
|
-
|
|
474
|
-
1. **Start with Material UI DatePicker** (most common in enterprise apps)
|
|
475
|
-
- Create `models/mui-datepicker-model.js`
|
|
476
|
-
- Create `utils/actions/mui-datepicker-action.js`
|
|
477
|
-
- Register in `models/index.js` and `index.js`
|
|
478
|
-
- Test on real MUI app
|
|
479
|
-
|
|
480
|
-
2. **Then react-datepicker** (simpler, widely used in React apps)
|
|
481
|
-
- Similar structure to MUI
|
|
482
|
-
- Test on react-datepicker demo
|
|
483
|
-
|
|
484
|
-
3. **Finally Ant Design** (most complex due to popup)
|
|
485
|
-
- Requires calendar interaction logic
|
|
486
|
-
- May need iterative refinement
|
|
487
|
-
|
|
488
|
-
4. **Future: Vuetify, PrimeReact, etc.**
|
|
489
|
-
- Follow same pattern as above
|
|
490
|
-
|
|
491
|
-
---
|
|
492
|
-
|
|
493
|
-
## Testing Strategy
|
|
494
|
-
|
|
495
|
-
1. **Manual testing** on real apps:
|
|
496
|
-
- Material UI Kitchen Sink demo
|
|
497
|
-
- Ant Design DatePicker demo
|
|
498
|
-
- react-datepicker Storybook
|
|
499
|
-
|
|
500
|
-
2. **Test cases** for each model:
|
|
501
|
-
- Detection (matches() returns true for correct elements)
|
|
502
|
-
- SetDate with various formats (YYYY-MM-DD, MM/DD/YYYY)
|
|
503
|
-
- SetDateTime with time
|
|
504
|
-
- Clear action
|
|
505
|
-
- Error handling (invalid dates)
|
|
506
|
-
|
|
507
|
-
3. **Cross-framework consistency**:
|
|
508
|
-
- Same API works across all DatePicker models
|
|
509
|
-
- analyzePage shows correct model name
|
|
510
|
-
- executeModelAction routes to correct handler
|
|
511
|
-
|
|
512
|
-
---
|
|
513
|
-
|
|
514
|
-
## Session Fixes Applied
|
|
515
|
-
|
|
516
|
-
### 1. executeCheckAction Implementation
|
|
517
|
-
- ✅ Created `utils/actions/check-action.js` with scrollIntoView + timeout wrapper
|
|
518
|
-
- ✅ Fixed timeout issue (required server restart)
|
|
519
|
-
- ✅ Tested with React TodoMVC checkboxes
|
|
520
|
-
|
|
521
|
-
### 2. Checkboxes Not Appearing in APOM Tree
|
|
522
|
-
- ✅ Root cause: `isVisible()` filtered `opacity:0` elements
|
|
523
|
-
- ✅ Fix: Added exception for stylable inputs (checkbox, radio, file)
|
|
524
|
-
- ✅ Code change in `pom/apom-tree-converter.js:432-443`
|
|
525
|
-
- ✅ Tested: Checkboxes now appear with model "Chk"
|
|
526
|
-
|
|
527
|
-
### 3. Browser Context Caching Issue
|
|
528
|
-
- ✅ Problem: `buildAPOMTree` loaded once via `eval()`, changes not applied
|
|
529
|
-
- ✅ Solution: Reload page after server restart to clear browser context
|
|
530
|
-
- ✅ Documented: Always refresh page after apom-tree-converter.js changes
|
|
531
|
-
|
|
532
|
-
---
|
|
533
|
-
|
|
534
|
-
## Notes
|
|
535
|
-
|
|
536
|
-
- **Priority system**: Higher priority models are checked first (e.g., MuiDatePickerModel before generic DatePickerModel)
|
|
537
|
-
- **Fallback**: If no specific model matches, element falls back to TextInputModel or DefaultModel
|
|
538
|
-
- **Extensibility**: Adding new UI framework = create new model class, no core changes needed
|
|
539
|
-
- **Testing**: Each model should be tested independently on framework-specific demo pages
|
|
540
|
-
|
|
541
|
-
---
|
|
542
|
-
|
|
543
|
-
**End of Document**
|
|
1
|
+
# DatePicker Models Implementation Plan
|
|
2
|
+
|
|
3
|
+
**Status**: Architecture approved, implementation pending
|
|
4
|
+
**Date**: 2026-02-15
|
|
5
|
+
**Decision**: Separate models for each UI framework (approved)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture Decision
|
|
10
|
+
|
|
11
|
+
### ✅ APPROVED: Separate Models for Each Framework
|
|
12
|
+
|
|
13
|
+
**Rationale**:
|
|
14
|
+
- DatePickers are fundamentally different across frameworks (input-based vs popup vs custom render)
|
|
15
|
+
- Impossible to create universal logic without brittle code
|
|
16
|
+
- Each framework has specific interactions and edge cases
|
|
17
|
+
- Easier to maintain, test, and extend
|
|
18
|
+
- API remains unified for AI - `executeModelAction` works the same way
|
|
19
|
+
|
|
20
|
+
**Structure**:
|
|
21
|
+
```
|
|
22
|
+
models/
|
|
23
|
+
├── mui-datepicker-model.js → MuiDatePickerModel
|
|
24
|
+
├── ant-datepicker-model.js → AntDatePickerModel
|
|
25
|
+
├── react-datepicker-model.js → ReactDatePickerModel
|
|
26
|
+
└── vuetify-datepicker-model.js → VuetifyDatePickerModel (future)
|
|
27
|
+
|
|
28
|
+
utils/actions/
|
|
29
|
+
├── mui-datepicker-action.js → executeMuiDatePickerAction
|
|
30
|
+
├── ant-datepicker-action.js → executeAntDatePickerAction
|
|
31
|
+
└── react-datepicker-action.js → executeReactDatePickerAction
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Current Status
|
|
37
|
+
|
|
38
|
+
### ✅ Implemented
|
|
39
|
+
|
|
40
|
+
1. **Model System with Strategy Pattern**
|
|
41
|
+
- ✅ `models/ElementModel.js` - base class
|
|
42
|
+
- ✅ `models/index.js` - 13 concrete models
|
|
43
|
+
- ✅ `models/ModelRegistry.js` - registry with findModel()
|
|
44
|
+
- ✅ Integration in `apom-tree-converter.js`
|
|
45
|
+
|
|
46
|
+
2. **executeModelAction Tool**
|
|
47
|
+
- ✅ Supports APOM ID (`id` parameter)
|
|
48
|
+
- ✅ Supports CSS selector (`selector` parameter)
|
|
49
|
+
- ✅ Routing via ModelRegistry.getActionHandler()
|
|
50
|
+
- ✅ Action parameters via `params`
|
|
51
|
+
|
|
52
|
+
3. **Action Handlers (Reusable)**
|
|
53
|
+
- ✅ `executeClickAction` - used by all models
|
|
54
|
+
- ✅ `executeTypeAction` - TxtInp, DateInp, Range, Color
|
|
55
|
+
- ✅ `executeHoverAction` - all models
|
|
56
|
+
- ✅ `executeScreenshotAction` - all models
|
|
57
|
+
- ✅ `executeSelectOptionAction` - Sel (select)
|
|
58
|
+
- ✅ `executeCheckAction` - Chk, Radio (check/uncheck/toggle)
|
|
59
|
+
|
|
60
|
+
4. **APOM Tree Integration**
|
|
61
|
+
- ✅ Models appear in `analyzePage` with model name
|
|
62
|
+
- ✅ `models` map shows available actions per model
|
|
63
|
+
- ✅ Checkboxes with `opacity:0` are now visible (fixed `isVisible()`)
|
|
64
|
+
|
|
65
|
+
5. **Material UI DatePicker Detection** (Session Fix)
|
|
66
|
+
- ✅ Updated `DatePickerModel.matches()` to detect MUI DatePicker
|
|
67
|
+
- ✅ Detection logic:
|
|
68
|
+
```javascript
|
|
69
|
+
if (classes.includes('MuiFormControl') || classes.includes('MuiTextField')) {
|
|
70
|
+
const hasInput = element.querySelector('input[type="text"]');
|
|
71
|
+
const hasCalendarIcon = element.querySelector('button[aria-label*="date"]');
|
|
72
|
+
return hasInput && hasCalendarIcon;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## ❌ NOT Implemented (TODO)
|
|
79
|
+
|
|
80
|
+
### 1. Material UI DatePicker Model & Action
|
|
81
|
+
|
|
82
|
+
**File**: `models/mui-datepicker-model.js`
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
class MuiDatePickerModel extends ElementModel {
|
|
86
|
+
getName() {
|
|
87
|
+
return 'MuiDatePicker';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getActions() {
|
|
91
|
+
return ['SetDate', 'SetDateTime', 'SetTime', 'click', 'clear'];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getPriority() {
|
|
95
|
+
return 100; // Check before generic DatePickerModel
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
matches(element, elementType) {
|
|
99
|
+
const classes = element.className || '';
|
|
100
|
+
|
|
101
|
+
// MUI v5/v6 DatePicker: MuiFormControl/MuiTextField + input + calendar button
|
|
102
|
+
if (classes.includes('MuiFormControl') || classes.includes('MuiTextField')) {
|
|
103
|
+
const hasInput = element.querySelector('input[type="text"]');
|
|
104
|
+
const hasCalendarIcon = element.querySelector('button[aria-label*="date" i], button[aria-label*="calendar" i]');
|
|
105
|
+
return hasInput && hasCalendarIcon;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getActionHandler(actionName) {
|
|
112
|
+
const handlers = {
|
|
113
|
+
'SetDate': 'executeMuiDatePickerAction',
|
|
114
|
+
'SetDateTime': 'executeMuiDatePickerAction',
|
|
115
|
+
'SetTime': 'executeMuiDatePickerAction',
|
|
116
|
+
'click': 'executeClickAction',
|
|
117
|
+
'clear': 'executeMuiDatePickerAction'
|
|
118
|
+
};
|
|
119
|
+
return handlers[actionName] || null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**File**: `utils/actions/mui-datepicker-action.js`
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
/**
|
|
128
|
+
* Material UI DatePicker Action Handler
|
|
129
|
+
* Handles SetDate, SetDateTime, SetTime, clear actions for MUI DatePicker
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
export async function executeMuiDatePickerAction(page, element, action, params) {
|
|
133
|
+
const identifier = params.identifier || 'MUI DatePicker';
|
|
134
|
+
|
|
135
|
+
if (action === 'SetDate' || action === 'SetDateTime') {
|
|
136
|
+
// Find input inside MUI DatePicker
|
|
137
|
+
const input = await element.$('input[type="text"]');
|
|
138
|
+
if (!input) {
|
|
139
|
+
throw new Error(`Input not found inside ${identifier}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Strategy 1: Direct input.value set (works for MUI v5/v6)
|
|
143
|
+
await input.evaluate((el, date) => {
|
|
144
|
+
el.value = date;
|
|
145
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
146
|
+
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
147
|
+
}, params.date || params.datetime);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{ type: "text", text: `Set ${action} to "${params.date || params.datetime}" in ${identifier}` }
|
|
152
|
+
]
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (action === 'SetTime') {
|
|
157
|
+
// MUI TimePicker logic (similar to SetDate)
|
|
158
|
+
const input = await element.$('input[type="text"]');
|
|
159
|
+
await input.evaluate((el, time) => {
|
|
160
|
+
el.value = time;
|
|
161
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
162
|
+
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
163
|
+
}, params.time);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
content: [
|
|
167
|
+
{ type: "text", text: `Set time to "${params.time}" in ${identifier}` }
|
|
168
|
+
]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (action === 'clear') {
|
|
173
|
+
const input = await element.$('input[type="text"]');
|
|
174
|
+
await input.evaluate(el => {
|
|
175
|
+
el.value = '';
|
|
176
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
177
|
+
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
content: [
|
|
182
|
+
{ type: "text", text: `Cleared ${identifier}` }
|
|
183
|
+
]
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
throw new Error(`Unknown action: ${action}`);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Registration**:
|
|
192
|
+
1. Add to `models/index.js` MODELS array
|
|
193
|
+
2. Add to `utils/actions/index.js` exports
|
|
194
|
+
3. Add to `index.js` imports and actionHandlers map
|
|
195
|
+
4. Add invocation logic in executeModelAction handler
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### 2. Ant Design DatePicker Model & Action
|
|
200
|
+
|
|
201
|
+
**File**: `models/ant-datepicker-model.js`
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
class AntDatePickerModel extends ElementModel {
|
|
205
|
+
getName() {
|
|
206
|
+
return 'AntDatePicker';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
getActions() {
|
|
210
|
+
return ['SetDate', 'SetRange', 'SetDateTime', 'click', 'clear'];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
getPriority() {
|
|
214
|
+
return 100;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
matches(element, elementType) {
|
|
218
|
+
const classes = element.className || '';
|
|
219
|
+
return classes.includes('ant-picker');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
getActionHandler(actionName) {
|
|
223
|
+
const handlers = {
|
|
224
|
+
'SetDate': 'executeAntDatePickerAction',
|
|
225
|
+
'SetRange': 'executeAntDatePickerAction',
|
|
226
|
+
'SetDateTime': 'executeAntDatePickerAction',
|
|
227
|
+
'click': 'executeClickAction',
|
|
228
|
+
'clear': 'executeAntDatePickerAction'
|
|
229
|
+
};
|
|
230
|
+
return handlers[actionName] || null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**File**: `utils/actions/ant-datepicker-action.js`
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
/**
|
|
239
|
+
* Ant Design DatePicker Action Handler
|
|
240
|
+
* Handles SetDate, SetRange, SetDateTime, clear actions for Ant Design DatePicker
|
|
241
|
+
*
|
|
242
|
+
* Note: Ant DatePicker uses popup calendar, requires complex interaction
|
|
243
|
+
*/
|
|
244
|
+
|
|
245
|
+
export async function executeAntDatePickerAction(page, element, action, params) {
|
|
246
|
+
const identifier = params.identifier || 'Ant DatePicker';
|
|
247
|
+
|
|
248
|
+
if (action === 'SetDate' || action === 'SetDateTime') {
|
|
249
|
+
// Strategy 1: Try direct input value set (works for some Ant versions)
|
|
250
|
+
const input = await element.$('input');
|
|
251
|
+
if (input) {
|
|
252
|
+
try {
|
|
253
|
+
await input.evaluate((el, date) => {
|
|
254
|
+
el.value = date;
|
|
255
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
256
|
+
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
257
|
+
}, params.date || params.datetime);
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
content: [
|
|
261
|
+
{ type: "text", text: `Set ${action} to "${params.date || params.datetime}" in ${identifier}` }
|
|
262
|
+
]
|
|
263
|
+
};
|
|
264
|
+
} catch (e) {
|
|
265
|
+
// Fallback to popup interaction
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Strategy 2: Popup interaction (more reliable but complex)
|
|
270
|
+
// 1. Click to open popup
|
|
271
|
+
await element.click();
|
|
272
|
+
await page.waitForSelector('.ant-picker-dropdown', { timeout: 2000 });
|
|
273
|
+
|
|
274
|
+
// 2. Parse date and navigate calendar
|
|
275
|
+
// ... (complex calendar interaction logic)
|
|
276
|
+
|
|
277
|
+
throw new Error('Popup interaction not implemented yet - use direct input strategy');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (action === 'SetRange') {
|
|
281
|
+
// Range picker specific logic (two dates)
|
|
282
|
+
throw new Error('SetRange action not implemented yet');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (action === 'clear') {
|
|
286
|
+
const clearBtn = await element.$('.ant-picker-clear');
|
|
287
|
+
if (clearBtn) {
|
|
288
|
+
await clearBtn.click();
|
|
289
|
+
} else {
|
|
290
|
+
const input = await element.$('input');
|
|
291
|
+
await input.evaluate(el => {
|
|
292
|
+
el.value = '';
|
|
293
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
content: [
|
|
299
|
+
{ type: "text", text: `Cleared ${identifier}` }
|
|
300
|
+
]
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
throw new Error(`Unknown action: ${action}`);
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### 3. react-datepicker Model & Action
|
|
311
|
+
|
|
312
|
+
**File**: `models/react-datepicker-model.js`
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
class ReactDatePickerModel extends ElementModel {
|
|
316
|
+
getName() {
|
|
317
|
+
return 'ReactDatePicker';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
getActions() {
|
|
321
|
+
return ['SetDate', 'SetDateTime', 'click', 'clear'];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
getPriority() {
|
|
325
|
+
return 100;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
matches(element, elementType) {
|
|
329
|
+
const classes = element.className || '';
|
|
330
|
+
return classes.includes('react-datepicker-wrapper') ||
|
|
331
|
+
classes.includes('react-datepicker__input-container');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
getActionHandler(actionName) {
|
|
335
|
+
const handlers = {
|
|
336
|
+
'SetDate': 'executeReactDatePickerAction',
|
|
337
|
+
'SetDateTime': 'executeReactDatePickerAction',
|
|
338
|
+
'click': 'executeClickAction',
|
|
339
|
+
'clear': 'executeReactDatePickerAction'
|
|
340
|
+
};
|
|
341
|
+
return handlers[actionName] || null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**File**: `utils/actions/react-datepicker-action.js`
|
|
347
|
+
|
|
348
|
+
```javascript
|
|
349
|
+
/**
|
|
350
|
+
* react-datepicker Action Handler
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
export async function executeReactDatePickerAction(page, element, action, params) {
|
|
354
|
+
const identifier = params.identifier || 'React DatePicker';
|
|
355
|
+
|
|
356
|
+
if (action === 'SetDate' || action === 'SetDateTime') {
|
|
357
|
+
const input = await element.$('input');
|
|
358
|
+
|
|
359
|
+
await input.evaluate((el, date) => {
|
|
360
|
+
el.value = date;
|
|
361
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
362
|
+
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
363
|
+
}, params.date || params.datetime);
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
content: [
|
|
367
|
+
{ type: "text", text: `Set ${action} to "${params.date || params.datetime}" in ${identifier}` }
|
|
368
|
+
]
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (action === 'clear') {
|
|
373
|
+
const input = await element.$('input');
|
|
374
|
+
await input.evaluate(el => {
|
|
375
|
+
el.value = '';
|
|
376
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
content: [
|
|
381
|
+
{ type: "text", text: `Cleared ${identifier}` }
|
|
382
|
+
]
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
throw new Error(`Unknown action: ${action}`);
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### 4. Update models/index.js
|
|
393
|
+
|
|
394
|
+
Remove generic DatePickerModel, add specific models:
|
|
395
|
+
|
|
396
|
+
```javascript
|
|
397
|
+
// Remove or deprecate:
|
|
398
|
+
// class DatePickerModel extends ElementModel { ... }
|
|
399
|
+
|
|
400
|
+
// Add new imports at the top:
|
|
401
|
+
import { MuiDatePickerModel } from './mui-datepicker-model.js';
|
|
402
|
+
import { AntDatePickerModel } from './ant-datepicker-model.js';
|
|
403
|
+
import { ReactDatePickerModel } from './react-datepicker-model.js';
|
|
404
|
+
|
|
405
|
+
// Update MODELS array:
|
|
406
|
+
const MODELS = [
|
|
407
|
+
TextInputModel,
|
|
408
|
+
SelectModel,
|
|
409
|
+
ButtonModel,
|
|
410
|
+
CheckboxModel,
|
|
411
|
+
RadioModel,
|
|
412
|
+
TextAreaModel,
|
|
413
|
+
LinkModel,
|
|
414
|
+
RangeInputModel,
|
|
415
|
+
MuiDatePickerModel, // ← Add
|
|
416
|
+
AntDatePickerModel, // ← Add
|
|
417
|
+
ReactDatePickerModel, // ← Add
|
|
418
|
+
DateInputModel, // Native HTML5 date input
|
|
419
|
+
FileInputModel,
|
|
420
|
+
ColorInputModel,
|
|
421
|
+
DefaultModel
|
|
422
|
+
];
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
### 5. Update index.js
|
|
428
|
+
|
|
429
|
+
**Imports** (around line 73):
|
|
430
|
+
```javascript
|
|
431
|
+
import {
|
|
432
|
+
executeClickAction,
|
|
433
|
+
executeTypeAction,
|
|
434
|
+
executeHoverAction,
|
|
435
|
+
executeScreenshotAction,
|
|
436
|
+
executeSelectOptionAction,
|
|
437
|
+
executeCheckAction,
|
|
438
|
+
executeMuiDatePickerAction, // ← Add
|
|
439
|
+
executeAntDatePickerAction, // ← Add
|
|
440
|
+
executeReactDatePickerAction // ← Add
|
|
441
|
+
} from './utils/actions/index.js';
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**actionHandlers map** (around line 633):
|
|
445
|
+
```javascript
|
|
446
|
+
const actionHandlers = {
|
|
447
|
+
'executeClickAction': executeClickAction,
|
|
448
|
+
'executeTypeAction': executeTypeAction,
|
|
449
|
+
'executeHoverAction': executeHoverAction,
|
|
450
|
+
'executeScreenshotAction': executeScreenshotAction,
|
|
451
|
+
'executeSelectOptionAction': executeSelectOptionAction,
|
|
452
|
+
'executeCheckAction': executeCheckAction,
|
|
453
|
+
'executeMuiDatePickerAction': executeMuiDatePickerAction, // ← Add
|
|
454
|
+
'executeAntDatePickerAction': executeAntDatePickerAction, // ← Add
|
|
455
|
+
'executeReactDatePickerAction': executeReactDatePickerAction // ← Add
|
|
456
|
+
};
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Invocation logic** (around line 653):
|
|
460
|
+
```javascript
|
|
461
|
+
// After executeCheckAction block
|
|
462
|
+
} else if (handlerInfo.handlerName === 'executeMuiDatePickerAction') {
|
|
463
|
+
result = await handlerFunction(page, element, validatedArgs.action, actionParams);
|
|
464
|
+
} else if (handlerInfo.handlerName === 'executeAntDatePickerAction') {
|
|
465
|
+
result = await handlerFunction(page, element, validatedArgs.action, actionParams);
|
|
466
|
+
} else if (handlerInfo.handlerName === 'executeReactDatePickerAction') {
|
|
467
|
+
result = await handlerFunction(page, element, validatedArgs.action, actionParams);
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Implementation Order (Recommended)
|
|
473
|
+
|
|
474
|
+
1. **Start with Material UI DatePicker** (most common in enterprise apps)
|
|
475
|
+
- Create `models/mui-datepicker-model.js`
|
|
476
|
+
- Create `utils/actions/mui-datepicker-action.js`
|
|
477
|
+
- Register in `models/index.js` and `index.js`
|
|
478
|
+
- Test on real MUI app
|
|
479
|
+
|
|
480
|
+
2. **Then react-datepicker** (simpler, widely used in React apps)
|
|
481
|
+
- Similar structure to MUI
|
|
482
|
+
- Test on react-datepicker demo
|
|
483
|
+
|
|
484
|
+
3. **Finally Ant Design** (most complex due to popup)
|
|
485
|
+
- Requires calendar interaction logic
|
|
486
|
+
- May need iterative refinement
|
|
487
|
+
|
|
488
|
+
4. **Future: Vuetify, PrimeReact, etc.**
|
|
489
|
+
- Follow same pattern as above
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## Testing Strategy
|
|
494
|
+
|
|
495
|
+
1. **Manual testing** on real apps:
|
|
496
|
+
- Material UI Kitchen Sink demo
|
|
497
|
+
- Ant Design DatePicker demo
|
|
498
|
+
- react-datepicker Storybook
|
|
499
|
+
|
|
500
|
+
2. **Test cases** for each model:
|
|
501
|
+
- Detection (matches() returns true for correct elements)
|
|
502
|
+
- SetDate with various formats (YYYY-MM-DD, MM/DD/YYYY)
|
|
503
|
+
- SetDateTime with time
|
|
504
|
+
- Clear action
|
|
505
|
+
- Error handling (invalid dates)
|
|
506
|
+
|
|
507
|
+
3. **Cross-framework consistency**:
|
|
508
|
+
- Same API works across all DatePicker models
|
|
509
|
+
- analyzePage shows correct model name
|
|
510
|
+
- executeModelAction routes to correct handler
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## Session Fixes Applied
|
|
515
|
+
|
|
516
|
+
### 1. executeCheckAction Implementation
|
|
517
|
+
- ✅ Created `utils/actions/check-action.js` with scrollIntoView + timeout wrapper
|
|
518
|
+
- ✅ Fixed timeout issue (required server restart)
|
|
519
|
+
- ✅ Tested with React TodoMVC checkboxes
|
|
520
|
+
|
|
521
|
+
### 2. Checkboxes Not Appearing in APOM Tree
|
|
522
|
+
- ✅ Root cause: `isVisible()` filtered `opacity:0` elements
|
|
523
|
+
- ✅ Fix: Added exception for stylable inputs (checkbox, radio, file)
|
|
524
|
+
- ✅ Code change in `pom/apom-tree-converter.js:432-443`
|
|
525
|
+
- ✅ Tested: Checkboxes now appear with model "Chk"
|
|
526
|
+
|
|
527
|
+
### 3. Browser Context Caching Issue
|
|
528
|
+
- ✅ Problem: `buildAPOMTree` loaded once via `eval()`, changes not applied
|
|
529
|
+
- ✅ Solution: Reload page after server restart to clear browser context
|
|
530
|
+
- ✅ Documented: Always refresh page after apom-tree-converter.js changes
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## Notes
|
|
535
|
+
|
|
536
|
+
- **Priority system**: Higher priority models are checked first (e.g., MuiDatePickerModel before generic DatePickerModel)
|
|
537
|
+
- **Fallback**: If no specific model matches, element falls back to TextInputModel or DefaultModel
|
|
538
|
+
- **Extensibility**: Adding new UI framework = create new model class, no core changes needed
|
|
539
|
+
- **Testing**: Each model should be tested independently on framework-specific demo pages
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
**End of Document**
|