@tayo-dev/rtl 1.0.0

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.
Files changed (150) hide show
  1. package/README.md +250 -0
  2. package/dist/analyzer/mocks/detector.d.ts +59 -0
  3. package/dist/analyzer/mocks/detector.d.ts.map +1 -0
  4. package/dist/analyzer/mocks/detector.js +264 -0
  5. package/dist/analyzer/mocks/detector.js.map +1 -0
  6. package/dist/analyzer/mocks/target-analyzer.d.ts +92 -0
  7. package/dist/analyzer/mocks/target-analyzer.d.ts.map +1 -0
  8. package/dist/analyzer/mocks/target-analyzer.js +305 -0
  9. package/dist/analyzer/mocks/target-analyzer.js.map +1 -0
  10. package/dist/analyzer/visual/element-analyzer.d.ts +44 -0
  11. package/dist/analyzer/visual/element-analyzer.d.ts.map +1 -0
  12. package/dist/analyzer/visual/element-analyzer.js +176 -0
  13. package/dist/analyzer/visual/element-analyzer.js.map +1 -0
  14. package/dist/analyzer/visual/inspector.d.ts +49 -0
  15. package/dist/analyzer/visual/inspector.d.ts.map +1 -0
  16. package/dist/analyzer/visual/inspector.js +109 -0
  17. package/dist/analyzer/visual/inspector.js.map +1 -0
  18. package/dist/cli/commands/generate.d.ts +13 -0
  19. package/dist/cli/commands/generate.d.ts.map +1 -0
  20. package/dist/cli/commands/generate.js +417 -0
  21. package/dist/cli/commands/generate.js.map +1 -0
  22. package/dist/core/generator.d.ts +32 -0
  23. package/dist/core/generator.d.ts.map +1 -0
  24. package/dist/core/generator.js +173 -0
  25. package/dist/core/generator.js.map +1 -0
  26. package/dist/core/js-parser.d.ts +48 -0
  27. package/dist/core/js-parser.d.ts.map +1 -0
  28. package/dist/core/js-parser.js +244 -0
  29. package/dist/core/js-parser.js.map +1 -0
  30. package/dist/core/mock-intelligence.d.ts +14 -0
  31. package/dist/core/mock-intelligence.d.ts.map +1 -0
  32. package/dist/core/mock-intelligence.js +140 -0
  33. package/dist/core/mock-intelligence.js.map +1 -0
  34. package/dist/core/orchestrator.d.ts +49 -0
  35. package/dist/core/orchestrator.d.ts.map +1 -0
  36. package/dist/core/orchestrator.js +315 -0
  37. package/dist/core/orchestrator.js.map +1 -0
  38. package/dist/core/parser.d.ts +9 -0
  39. package/dist/core/parser.d.ts.map +1 -0
  40. package/dist/core/parser.js +120 -0
  41. package/dist/core/parser.js.map +1 -0
  42. package/dist/core/recording-intelligence.d.ts +15 -0
  43. package/dist/core/recording-intelligence.d.ts.map +1 -0
  44. package/dist/core/recording-intelligence.js +178 -0
  45. package/dist/core/recording-intelligence.js.map +1 -0
  46. package/dist/core/resolver.d.ts +58 -0
  47. package/dist/core/resolver.d.ts.map +1 -0
  48. package/dist/core/resolver.js +291 -0
  49. package/dist/core/resolver.js.map +1 -0
  50. package/dist/core/scanner.d.ts +51 -0
  51. package/dist/core/scanner.d.ts.map +1 -0
  52. package/dist/core/scanner.js +310 -0
  53. package/dist/core/scanner.js.map +1 -0
  54. package/dist/core/scorer.d.ts +8 -0
  55. package/dist/core/scorer.d.ts.map +1 -0
  56. package/dist/core/scorer.js +76 -0
  57. package/dist/core/scorer.js.map +1 -0
  58. package/dist/core/validator.d.ts +134 -0
  59. package/dist/core/validator.d.ts.map +1 -0
  60. package/dist/core/validator.js +44 -0
  61. package/dist/core/validator.js.map +1 -0
  62. package/dist/core/verifier.d.ts +10 -0
  63. package/dist/core/verifier.d.ts.map +1 -0
  64. package/dist/core/verifier.js +30 -0
  65. package/dist/core/verifier.js.map +1 -0
  66. package/dist/core/writer.d.ts +15 -0
  67. package/dist/core/writer.d.ts.map +1 -0
  68. package/dist/core/writer.js +43 -0
  69. package/dist/core/writer.js.map +1 -0
  70. package/dist/generator/mocks/builder.d.ts +47 -0
  71. package/dist/generator/mocks/builder.d.ts.map +1 -0
  72. package/dist/generator/mocks/builder.js +335 -0
  73. package/dist/generator/mocks/builder.js.map +1 -0
  74. package/dist/generator/transforms/dialog-transform.d.ts +35 -0
  75. package/dist/generator/transforms/dialog-transform.d.ts.map +1 -0
  76. package/dist/generator/transforms/dialog-transform.js +293 -0
  77. package/dist/generator/transforms/dialog-transform.js.map +1 -0
  78. package/dist/index.d.ts +7 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +18 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/learner/analyzer.d.ts +13 -0
  83. package/dist/learner/analyzer.d.ts.map +1 -0
  84. package/dist/learner/analyzer.js +484 -0
  85. package/dist/learner/analyzer.js.map +1 -0
  86. package/dist/learner/index.d.ts +66 -0
  87. package/dist/learner/index.d.ts.map +1 -0
  88. package/dist/learner/index.js +247 -0
  89. package/dist/learner/index.js.map +1 -0
  90. package/dist/learner/storage.d.ts +68 -0
  91. package/dist/learner/storage.d.ts.map +1 -0
  92. package/dist/learner/storage.js +201 -0
  93. package/dist/learner/storage.js.map +1 -0
  94. package/dist/learner/types.d.ts +41 -0
  95. package/dist/learner/types.d.ts.map +1 -0
  96. package/dist/learner/types.js +31 -0
  97. package/dist/learner/types.js.map +1 -0
  98. package/dist/parser/recorder-parser.d.ts +40 -0
  99. package/dist/parser/recorder-parser.d.ts.map +1 -0
  100. package/dist/parser/recorder-parser.js +139 -0
  101. package/dist/parser/recorder-parser.js.map +1 -0
  102. package/dist/parser/steps/deduplicator.d.ts +19 -0
  103. package/dist/parser/steps/deduplicator.d.ts.map +1 -0
  104. package/dist/parser/steps/deduplicator.js +75 -0
  105. package/dist/parser/steps/deduplicator.js.map +1 -0
  106. package/dist/parser/steps/dialog-detector.d.ts +38 -0
  107. package/dist/parser/steps/dialog-detector.d.ts.map +1 -0
  108. package/dist/parser/steps/dialog-detector.js +290 -0
  109. package/dist/parser/steps/dialog-detector.js.map +1 -0
  110. package/dist/parser/steps/noise-filter.d.ts +21 -0
  111. package/dist/parser/steps/noise-filter.d.ts.map +1 -0
  112. package/dist/parser/steps/noise-filter.js +138 -0
  113. package/dist/parser/steps/noise-filter.js.map +1 -0
  114. package/dist/scorer/index.d.ts +43 -0
  115. package/dist/scorer/index.d.ts.map +1 -0
  116. package/dist/scorer/index.js +82 -0
  117. package/dist/scorer/index.js.map +1 -0
  118. package/dist/scorer/post-verify.d.ts +17 -0
  119. package/dist/scorer/post-verify.d.ts.map +1 -0
  120. package/dist/scorer/post-verify.js +163 -0
  121. package/dist/scorer/post-verify.js.map +1 -0
  122. package/dist/scorer/pre-audit.d.ts +32 -0
  123. package/dist/scorer/pre-audit.d.ts.map +1 -0
  124. package/dist/scorer/pre-audit.js +99 -0
  125. package/dist/scorer/pre-audit.js.map +1 -0
  126. package/dist/scorer/quality-gates.d.ts +17 -0
  127. package/dist/scorer/quality-gates.d.ts.map +1 -0
  128. package/dist/scorer/quality-gates.js +304 -0
  129. package/dist/scorer/quality-gates.js.map +1 -0
  130. package/dist/scorer/types.d.ts +27 -0
  131. package/dist/scorer/types.d.ts.map +1 -0
  132. package/dist/scorer/types.js +5 -0
  133. package/dist/scorer/types.js.map +1 -0
  134. package/dist/templates/test-template.d.ts +21 -0
  135. package/dist/templates/test-template.d.ts.map +1 -0
  136. package/dist/templates/test-template.js +92 -0
  137. package/dist/templates/test-template.js.map +1 -0
  138. package/dist/types/conventions.d.ts +49 -0
  139. package/dist/types/conventions.d.ts.map +1 -0
  140. package/dist/types/conventions.js +13 -0
  141. package/dist/types/conventions.js.map +1 -0
  142. package/dist/types/recording.d.ts +143 -0
  143. package/dist/types/recording.d.ts.map +1 -0
  144. package/dist/types/recording.js +5 -0
  145. package/dist/types/recording.js.map +1 -0
  146. package/dist/types/score.d.ts +18 -0
  147. package/dist/types/score.d.ts.map +1 -0
  148. package/dist/types/score.js +2 -0
  149. package/dist/types/score.js.map +1 -0
  150. package/package.json +51 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Recorder parser - Main entry point for parsing Chrome Recorder exports
3
+ *
4
+ * Pipeline:
5
+ * 1. Parse JSON → NormalizedSteps
6
+ * 2. deduplicateSteps(NormalizedSteps) → DedupedSteps
7
+ * 3. filterNoiseSteps(DedupedSteps) → CleanSteps
8
+ * 4. groupDialogSteps(CleanSteps) → DialogFlows
9
+ * 5. Continue to generation
10
+ */
11
+ import { readFile } from 'fs/promises';
12
+ import { resolve } from 'path';
13
+ import { deduplicateSteps } from './steps/deduplicator.js';
14
+ import { filterNoiseSteps } from './steps/noise-filter.js';
15
+ import { groupDialogSteps } from './steps/dialog-detector.js';
16
+ let stepIdCounter = 0;
17
+ /**
18
+ * Generate unique step IDs
19
+ */
20
+ function generateStepId() {
21
+ return `step_${++stepIdCounter}`;
22
+ }
23
+ /**
24
+ * Map Chrome Recorder step type to action name
25
+ */
26
+ function getActionName(type) {
27
+ const actionMap = {
28
+ click: 'click',
29
+ fill: 'fill',
30
+ select: 'select',
31
+ scroll: 'scroll',
32
+ assert: 'assert',
33
+ waitForSelector: 'waitForSelector',
34
+ doubleClick: 'doubleClick',
35
+ keyDown: 'keyDown',
36
+ navigate: 'navigate'
37
+ };
38
+ return actionMap[type] || type;
39
+ }
40
+ /**
41
+ * Extract selector from Chrome step
42
+ */
43
+ function extractSelector(step) {
44
+ if (step.selectors && step.selectors.length > 0) {
45
+ const firstSelectorArray = step.selectors[0];
46
+ if (firstSelectorArray && firstSelectorArray.length > 0) {
47
+ return firstSelectorArray[0];
48
+ }
49
+ }
50
+ return step.target;
51
+ }
52
+ /**
53
+ * Normalize a single Chrome step to internal format
54
+ */
55
+ function normalizeStep(step, index) {
56
+ const normalized = {
57
+ id: generateStepId(),
58
+ type: step.type,
59
+ action: getActionName(step.type),
60
+ target: step.target || '',
61
+ originalType: step.type,
62
+ selector: extractSelector(step),
63
+ timestamp: step.modifiedTime,
64
+ metadata: {}
65
+ };
66
+ if (step.value !== undefined) {
67
+ normalized.value = step.value;
68
+ }
69
+ if (step.assert) {
70
+ normalized.metadata = {
71
+ ...normalized.metadata,
72
+ assertExpression: step.assert.expression
73
+ };
74
+ }
75
+ if (step.url) {
76
+ normalized.metadata = {
77
+ ...normalized.metadata,
78
+ url: step.url
79
+ };
80
+ }
81
+ return normalized;
82
+ }
83
+ /**
84
+ * Parse Chrome Recorder JSON file and apply deduplication and noise filtering
85
+ *
86
+ * @param filePath - Path to Chrome Recorder JSON export
87
+ * @returns Normalized recording with cleaned steps
88
+ */
89
+ export async function parseRecording(filePath) {
90
+ const absolutePath = resolve(process.cwd(), filePath);
91
+ const content = await readFile(absolutePath, 'utf-8');
92
+ let exportData;
93
+ try {
94
+ exportData = JSON.parse(content);
95
+ }
96
+ catch (error) {
97
+ throw new Error(`Invalid JSON in ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
98
+ }
99
+ if (!exportData.steps || !Array.isArray(exportData.steps)) {
100
+ throw new Error(`Invalid Chrome Recorder export: missing or invalid "steps" array`);
101
+ }
102
+ // Reset step counter for each new recording
103
+ stepIdCounter = 0;
104
+ // Step 1: Normalize steps
105
+ let steps = exportData.steps.map((step, index) => normalizeStep(step, index));
106
+ // Step 2: Deduplicate rapid clicks (must run first)
107
+ steps = deduplicateSteps(steps);
108
+ // Step 3: Filter noise events
109
+ steps = filterNoiseSteps(steps);
110
+ return {
111
+ title: exportData.title || 'Untitled Recording',
112
+ steps,
113
+ rawStepCount: exportData.steps.length,
114
+ url: exportData.settings?.url,
115
+ settings: exportData.settings
116
+ };
117
+ }
118
+ /**
119
+ * Reset step counter (useful for testing)
120
+ */
121
+ export function resetStepCounter() {
122
+ stepIdCounter = 0;
123
+ }
124
+ // Re-export for convenience
125
+ export { deduplicateSteps } from './steps/deduplicator.js';
126
+ export { filterNoiseSteps } from './steps/noise-filter.js';
127
+ export { groupDialogSteps, resetDialogIdCounter } from './steps/dialog-detector.js';
128
+ /**
129
+ * Parse recording and extract dialog flows
130
+ *
131
+ * @param filePath - Path to Chrome Recorder JSON export
132
+ * @returns Object with normalized recording and detected dialog flows
133
+ */
134
+ export async function parseRecordingWithDialogs(filePath) {
135
+ const recording = await parseRecording(filePath);
136
+ const dialogFlows = groupDialogSteps(recording.steps);
137
+ return { recording, dialogFlows };
138
+ }
139
+ //# sourceMappingURL=recorder-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder-parser.js","sourceRoot":"","sources":["../../src/parser/recorder-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Click deduplicator - removes rapid duplicate clicks on the same element
3
+ *
4
+ * Algorithm:
5
+ * 1. Iterate through steps in order
6
+ * 2. For each click step, look ahead up to 500ms
7
+ * 3. If matching selector found, mark as duplicate
8
+ * 4. Return filtered list preserving original order
9
+ */
10
+ import type { RecordingStep } from '../../types/recording.js';
11
+ /**
12
+ * Deduplicate rapid clicks on the same element
13
+ *
14
+ * @param steps - Array of recording steps
15
+ * @returns Filtered array with duplicate clicks removed
16
+ */
17
+ export declare function deduplicateSteps(steps: RecordingStep[]): RecordingStep[];
18
+ export { deduplicateSteps as default };
19
+ //# sourceMappingURL=deduplicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deduplicator.d.ts","sourceRoot":"","sources":["../../../src/parser/steps/deduplicator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAgC9D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAsCxE;AAED,OAAO,EAAE,gBAAgB,IAAI,OAAO,EAAE,CAAC"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Click deduplicator - removes rapid duplicate clicks on the same element
3
+ *
4
+ * Algorithm:
5
+ * 1. Iterate through steps in order
6
+ * 2. For each click step, look ahead up to 500ms
7
+ * 3. If matching selector found, mark as duplicate
8
+ * 4. Return filtered list preserving original order
9
+ */
10
+ const RAPID_CLICK_THRESHOLD_MS = 500;
11
+ /**
12
+ * Check if a step is a click type
13
+ */
14
+ function isClickStep(step) {
15
+ return step.type === 'click' || step.type === 'doubleClick';
16
+ }
17
+ /**
18
+ * Check if two steps target the same element
19
+ */
20
+ function hasSameTarget(step1, step2) {
21
+ // Compare by selector or target
22
+ const target1 = step1.selector || step1.target;
23
+ const target2 = step2.selector || step2.target;
24
+ return target1 === target2 && target1 !== '';
25
+ }
26
+ /**
27
+ * Check if two steps are within the rapid-click threshold
28
+ */
29
+ function isWithinThreshold(step1, step2) {
30
+ if (step1.timestamp === undefined || step2.timestamp === undefined) {
31
+ // If no timestamps, assume they could be rapid (conservative)
32
+ return true;
33
+ }
34
+ return Math.abs(step2.timestamp - step1.timestamp) <= RAPID_CLICK_THRESHOLD_MS;
35
+ }
36
+ /**
37
+ * Deduplicate rapid clicks on the same element
38
+ *
39
+ * @param steps - Array of recording steps
40
+ * @returns Filtered array with duplicate clicks removed
41
+ */
42
+ export function deduplicateSteps(steps) {
43
+ if (!steps || steps.length === 0) {
44
+ return [];
45
+ }
46
+ // Track which steps are duplicates
47
+ const duplicateIds = new Set();
48
+ for (let i = 0; i < steps.length; i++) {
49
+ const currentStep = steps[i];
50
+ // Only consider click steps for deduplication
51
+ if (!isClickStep(currentStep)) {
52
+ continue;
53
+ }
54
+ // Look ahead for potential duplicates
55
+ for (let j = i + 1; j < steps.length; j++) {
56
+ const lookAheadStep = steps[j];
57
+ // Stop looking if we've gone past the threshold time
58
+ if (currentStep.timestamp !== undefined &&
59
+ lookAheadStep.timestamp !== undefined &&
60
+ lookAheadStep.timestamp - currentStep.timestamp > RAPID_CLICK_THRESHOLD_MS) {
61
+ break;
62
+ }
63
+ // Check if this is a duplicate click on the same target
64
+ if (isClickStep(lookAheadStep) &&
65
+ hasSameTarget(currentStep, lookAheadStep) &&
66
+ isWithinThreshold(currentStep, lookAheadStep)) {
67
+ duplicateIds.add(lookAheadStep.id);
68
+ }
69
+ }
70
+ }
71
+ // Filter out duplicates, preserving original order
72
+ return steps.filter(step => !duplicateIds.has(step.id));
73
+ }
74
+ export { deduplicateSteps as default };
75
+ //# sourceMappingURL=deduplicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deduplicator.js","sourceRoot":"","sources":["../../../src/parser/steps/deduplicator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAErC;;GAEG;AACH,SAAS,WAAW,CAAC,IAAmB;IACtC,OAAO,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,KAAoB,EAAE,KAAoB;IAC/D,gCAAgC;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;IAC/C,OAAO,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,EAAE,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,KAAoB,EAAE,KAAoB;IACnE,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACnE,8DAA8D;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,wBAAwB,CAAC;AACjF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,mCAAmC;IACnC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IAEvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAE7B,8CAA8C;QAC9C,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;YAC9B,SAAS;QACX,CAAC;QAED,sCAAsC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAE/B,qDAAqD;YACrD,IAAI,WAAW,CAAC,SAAS,KAAK,SAAS;gBACnC,aAAa,CAAC,SAAS,KAAK,SAAS;gBACrC,aAAa,CAAC,SAAS,GAAG,WAAW,CAAC,SAAS,GAAG,wBAAwB,EAAE,CAAC;gBAC/E,MAAM;YACR,CAAC;YAED,wDAAwD;YACxD,IAAI,WAAW,CAAC,aAAa,CAAC;gBAC1B,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC;gBACzC,iBAAiB,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,CAAC;gBAClD,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,OAAO,EAAE,gBAAgB,IAAI,OAAO,EAAE,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Dialog flow detector - detects and groups multi-step dialog interactions
3
+ *
4
+ * This module identifies dialog open/close flows and other multi-step interactions
5
+ * as logical units, improving test readability and reducing redundant assertions.
6
+ *
7
+ * Detection patterns:
8
+ * - Open dialog: click on modal trigger → dialog appears
9
+ * - Fill dialog: fill form fields in dialog
10
+ * - Submit dialog: click submit/confirm in dialog
11
+ * - Close dialog: click close/ESC/cancel outside
12
+ *
13
+ * Time window: related steps within 30s are grouped
14
+ */
15
+ import type { RecordingStep } from '../../types/recording.js';
16
+ export type DialogType = 'modal' | 'drawer' | 'popover' | 'confirm' | 'form';
17
+ export interface DialogFlow {
18
+ id: string;
19
+ type: DialogType;
20
+ triggerStep: RecordingStep;
21
+ contentSteps: RecordingStep[];
22
+ closeStep?: RecordingStep;
23
+ assertionStep?: RecordingStep;
24
+ timestamp: number;
25
+ }
26
+ /**
27
+ * Group recording steps into dialog flows
28
+ *
29
+ * @param steps - Array of recording steps (should be after deduplication and noise filtering)
30
+ * @returns Array of detected dialog flows
31
+ */
32
+ export declare function groupDialogSteps(steps: RecordingStep[]): DialogFlow[];
33
+ /**
34
+ * Reset dialog ID counter (useful for testing)
35
+ */
36
+ export declare function resetDialogIdCounter(): void;
37
+ export default groupDialogSteps;
38
+ //# sourceMappingURL=dialog-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-detector.d.ts","sourceRoot":"","sources":["../../../src/parser/steps/dialog-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,0BAA0B,CAAC;AAExE,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7E,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,aAAa,CAAC;IAC3B,YAAY,EAAE,aAAa,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAoND;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,UAAU,EAAE,CA4FrE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Dialog flow detector - detects and groups multi-step dialog interactions
3
+ *
4
+ * This module identifies dialog open/close flows and other multi-step interactions
5
+ * as logical units, improving test readability and reducing redundant assertions.
6
+ *
7
+ * Detection patterns:
8
+ * - Open dialog: click on modal trigger → dialog appears
9
+ * - Fill dialog: fill form fields in dialog
10
+ * - Submit dialog: click submit/confirm in dialog
11
+ * - Close dialog: click close/ESC/cancel outside
12
+ *
13
+ * Time window: related steps within 30s are grouped
14
+ */
15
+ const DIALOG_TIME_WINDOW_MS = 30000; // 30 seconds
16
+ /**
17
+ * Dialog-related selectors that indicate a dialog is opening
18
+ */
19
+ const DIALOG_TRIGGER_SELECTORS = [
20
+ '[data-testid*="modal"]',
21
+ '[data-testid*="dialog"]',
22
+ '[data-testid*="drawer"]',
23
+ '[data-testid*="popover"]',
24
+ '[aria-haspopup]',
25
+ '[role="dialog"]',
26
+ '[role="dialog"][aria-modal="true"]',
27
+ ];
28
+ /**
29
+ * Selectors that indicate a dialog close action
30
+ */
31
+ const DIALOG_CLOSE_SELECTORS = [
32
+ '.close',
33
+ '[class*="close"]',
34
+ '[aria-label="Close"]',
35
+ '[aria-label="close"]',
36
+ '[aria-label="Close dialog"]',
37
+ '[data-testid*="close"]',
38
+ '[data-testid*="Cancel"]',
39
+ '[role="button"][aria-label="Close"]',
40
+ ];
41
+ /**
42
+ * Dialog content selectors that indicate dialog is open
43
+ */
44
+ const DIALOG_CONTENT_SELECTORS = [
45
+ '[role="dialog"]',
46
+ '[role="dialog"][aria-modal="true"]',
47
+ '[role="alertdialog"]',
48
+ '[data-testid*="modal"]',
49
+ '[data-testid*="dialog"]',
50
+ '[data-testid*="drawer"]',
51
+ '[class*="modal"]',
52
+ '[class*="dialog"]',
53
+ '[class*="drawer"]',
54
+ ];
55
+ /**
56
+ * Check if a selector is a dialog trigger
57
+ */
58
+ function isDialogTrigger(step) {
59
+ const selector = step.selector || step.target || '';
60
+ const lowerSelector = selector.toLowerCase();
61
+ // Check explicit selectors
62
+ for (const triggerSelector of DIALOG_TRIGGER_SELECTORS) {
63
+ if (selectorMatches(selector, triggerSelector)) {
64
+ return true;
65
+ }
66
+ }
67
+ // Check for common trigger patterns in selector
68
+ const triggerPatterns = ['modal', 'dialog', 'drawer', 'popover', 'open', 'show'];
69
+ return triggerPatterns.some(pattern => lowerSelector.includes(pattern));
70
+ }
71
+ /**
72
+ * Check if a selector matches a pattern
73
+ */
74
+ function selectorMatches(selector, pattern) {
75
+ if (!selector || !pattern)
76
+ return false;
77
+ // Handle attribute selectors
78
+ if (pattern.startsWith('[')) {
79
+ return selector.includes(pattern);
80
+ }
81
+ // Simple substring match for class/ID selectors
82
+ return selector.includes(pattern);
83
+ }
84
+ /**
85
+ * Check if a step is a dialog close action
86
+ */
87
+ function isDialogCloseAction(step) {
88
+ const action = step.action?.toLowerCase() || '';
89
+ const selector = step.selector || step.target || '';
90
+ const lowerSelector = selector.toLowerCase();
91
+ // Check explicit close selectors
92
+ for (const closeSelector of DIALOG_CLOSE_SELECTORS) {
93
+ if (selectorMatches(selector, closeSelector)) {
94
+ return true;
95
+ }
96
+ }
97
+ // Check for cancel/close in action or selector
98
+ const closePatterns = ['close', 'cancel', 'dismiss', 'esc', 'escape'];
99
+ if (closePatterns.some(pattern => action.includes(pattern) || lowerSelector.includes(pattern))) {
100
+ return true;
101
+ }
102
+ // Check for ESC key press
103
+ if (step.type === 'keyDown' && (step.value === 'Escape' || step.value === 'Esc')) {
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+ /**
109
+ * Check if a step is a click on the dialog overlay (outside the dialog content)
110
+ */
111
+ function isDialogOverlayClick(step, dialogOpen) {
112
+ if (!dialogOpen)
113
+ return false;
114
+ // Click on body or html typically means clicking outside dialog
115
+ const selector = step.selector || step.target || '';
116
+ const lowerSelector = selector.toLowerCase();
117
+ return (selector === 'body' ||
118
+ selector === 'html' ||
119
+ lowerSelector.includes('overlay') ||
120
+ lowerSelector.includes('backdrop'));
121
+ }
122
+ /**
123
+ * Check if a step appears to be filling dialog content
124
+ */
125
+ function isDialogContentStep(step) {
126
+ // Fill, select, or keyDown in form elements
127
+ if (step.type === 'fill' || step.type === 'select') {
128
+ return true;
129
+ }
130
+ // KeyDown for typing in input fields
131
+ if (step.type === 'keyDown') {
132
+ const selector = step.selector || step.target || '';
133
+ const lowerSelector = selector.toLowerCase();
134
+ // Input, textarea, or contentEditable elements
135
+ return (lowerSelector.includes('input') ||
136
+ lowerSelector.includes('textarea') ||
137
+ lowerSelector.includes('contenteditable'));
138
+ }
139
+ return false;
140
+ }
141
+ /**
142
+ * Check if step appears to be an assertion about dialog state
143
+ */
144
+ function isDialogAssertion(step) {
145
+ if (step.type !== 'assert' && step.type !== 'waitForSelector') {
146
+ return false;
147
+ }
148
+ const selector = step.selector || step.target || '';
149
+ const lowerSelector = selector.toLowerCase();
150
+ // Check if assertion targets dialog elements
151
+ for (const contentSelector of DIALOG_CONTENT_SELECTORS) {
152
+ if (selectorMatches(selector, contentSelector)) {
153
+ return true;
154
+ }
155
+ }
156
+ // Check for dialog-related keywords
157
+ const dialogKeywords = ['modal', 'dialog', 'drawer', 'popover', 'visible', 'open'];
158
+ return dialogKeywords.some(keyword => lowerSelector.includes(keyword));
159
+ }
160
+ /**
161
+ * Determine the dialog type based on content
162
+ */
163
+ function inferDialogType(steps) {
164
+ // Check selectors for type hints
165
+ for (const step of steps) {
166
+ const selector = step.selector || step.target || '';
167
+ const lowerSelector = selector.toLowerCase();
168
+ if (lowerSelector.includes('drawer'))
169
+ return 'drawer';
170
+ if (lowerSelector.includes('popover'))
171
+ return 'popover';
172
+ if (lowerSelector.includes('confirm') || lowerSelector.includes('alert'))
173
+ return 'confirm';
174
+ }
175
+ // Default to modal
176
+ return 'modal';
177
+ }
178
+ /**
179
+ * Check if two steps are within the dialog time window
180
+ */
181
+ function isWithinTimeWindow(step1, step2) {
182
+ if (step1.timestamp === undefined || step2.timestamp === undefined) {
183
+ // If no timestamps, assume they could be related (conservative)
184
+ return true;
185
+ }
186
+ return Math.abs(step2.timestamp - step1.timestamp) <= DIALOG_TIME_WINDOW_MS;
187
+ }
188
+ /**
189
+ * Generate a unique ID for a dialog flow
190
+ */
191
+ let dialogIdCounter = 0;
192
+ function generateDialogId() {
193
+ return `dialog_${++dialogIdCounter}`;
194
+ }
195
+ /**
196
+ * Group recording steps into dialog flows
197
+ *
198
+ * @param steps - Array of recording steps (should be after deduplication and noise filtering)
199
+ * @returns Array of detected dialog flows
200
+ */
201
+ export function groupDialogSteps(steps) {
202
+ if (!steps || steps.length === 0) {
203
+ return [];
204
+ }
205
+ const dialogFlows = [];
206
+ let currentDialog = null;
207
+ let dialogOpen = false;
208
+ for (let i = 0; i < steps.length; i++) {
209
+ const step = steps[i];
210
+ // Skip if not a relevant step type
211
+ if (!['click', 'fill', 'select', 'keyDown', 'assert', 'waitForSelector'].includes(step.type)) {
212
+ continue;
213
+ }
214
+ // Case 1: Dialog trigger click - start a new dialog flow
215
+ if (step.type === 'click' && isDialogTrigger(step)) {
216
+ // Close any existing dialog flow
217
+ if (currentDialog) {
218
+ dialogFlows.push(currentDialog);
219
+ }
220
+ currentDialog = {
221
+ id: generateDialogId(),
222
+ type: inferDialogType([step]),
223
+ triggerStep: step,
224
+ contentSteps: [],
225
+ timestamp: step.timestamp || Date.now(),
226
+ };
227
+ dialogOpen = true;
228
+ continue;
229
+ }
230
+ // If we have an active dialog flow
231
+ if (currentDialog) {
232
+ // Case 2: Dialog close action - end the current flow
233
+ if (isDialogCloseAction(step) || isDialogOverlayClick(step, dialogOpen)) {
234
+ currentDialog.closeStep = step;
235
+ dialogFlows.push(currentDialog);
236
+ currentDialog = null;
237
+ dialogOpen = false;
238
+ continue;
239
+ }
240
+ // Case 3: Dialog content step - add to current flow
241
+ if (isDialogContentStep(step)) {
242
+ // Check time window - if too far, might be separate interaction
243
+ if (isWithinTimeWindow(currentDialog.triggerStep, step)) {
244
+ currentDialog.contentSteps.push(step);
245
+ continue;
246
+ }
247
+ }
248
+ // Case 4: Dialog assertion - mark for verification
249
+ if (isDialogAssertion(step)) {
250
+ currentDialog.assertionStep = step;
251
+ continue;
252
+ }
253
+ // Case 5: Non-dialog click - might close dialog and end flow
254
+ if (step.type === 'click' && !isDialogTrigger(step)) {
255
+ // Check if this might be closing the dialog
256
+ if (dialogOpen) {
257
+ currentDialog.closeStep = step;
258
+ dialogFlows.push(currentDialog);
259
+ currentDialog = null;
260
+ dialogOpen = false;
261
+ continue;
262
+ }
263
+ }
264
+ // Check time window - if step is too far from dialog start, end the flow
265
+ if (!isWithinTimeWindow(currentDialog.triggerStep, step)) {
266
+ if (currentDialog.contentSteps.length > 0 || currentDialog.closeStep) {
267
+ dialogFlows.push(currentDialog);
268
+ }
269
+ else {
270
+ // No meaningful content, discard
271
+ }
272
+ currentDialog = null;
273
+ dialogOpen = false;
274
+ }
275
+ }
276
+ }
277
+ // Don't forget the last dialog if still open
278
+ if (currentDialog) {
279
+ dialogFlows.push(currentDialog);
280
+ }
281
+ return dialogFlows;
282
+ }
283
+ /**
284
+ * Reset dialog ID counter (useful for testing)
285
+ */
286
+ export function resetDialogIdCounter() {
287
+ dialogIdCounter = 0;
288
+ }
289
+ export default groupDialogSteps;
290
+ //# sourceMappingURL=dialog-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-detector.js","sourceRoot":"","sources":["../../../src/parser/steps/dialog-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Noise event filter - removes irrelevant events from recordings
3
+ *
4
+ * Filters out:
5
+ * - dblClick events (double clicks)
6
+ * - mousemove/mouseover/mouseout (cursor wandering)
7
+ * - Accidental scroll events (scroll with no action within 2s)
8
+ *
9
+ * Preserves:
10
+ * - click, fill, select, change, navigate, keyPress, assert
11
+ */
12
+ import type { RecordingStep } from '../../types/recording.js';
13
+ /**
14
+ * Filter noise events from recording steps
15
+ *
16
+ * @param steps - Array of recording steps
17
+ * @returns Filtered array with noise events removed
18
+ */
19
+ export declare function filterNoiseSteps(steps: RecordingStep[]): RecordingStep[];
20
+ export { filterNoiseSteps as default };
21
+ //# sourceMappingURL=noise-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noise-filter.d.ts","sourceRoot":"","sources":["../../../src/parser/steps/noise-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,0BAA0B,CAAC;AAqGxE;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAuCxE;AAED,OAAO,EAAE,gBAAgB,IAAI,OAAO,EAAE,CAAC"}