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.
@@ -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**