appium-session-recorder 0.0.1 → 0.0.3

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 (74) hide show
  1. package/README.md +34 -16
  2. package/dist/index.js +32422 -0
  3. package/dist/ui/assets/index-CUcJNRfB.css +1 -0
  4. package/dist/ui/assets/index-Cl_X3tPj.js +4 -0
  5. package/{src → dist}/ui/index.html +2 -1
  6. package/package.json +10 -3
  7. package/bun.lock +0 -731
  8. package/src/cli/arg-parser.ts +0 -311
  9. package/src/cli/commands/drive.ts +0 -147
  10. package/src/cli/commands/index.ts +0 -54
  11. package/src/cli/commands/proxy.ts +0 -41
  12. package/src/cli/commands/screen.ts +0 -73
  13. package/src/cli/commands/selectors.ts +0 -42
  14. package/src/cli/commands/session.ts +0 -64
  15. package/src/cli/commands/types.ts +0 -11
  16. package/src/cli/index.ts +0 -158
  17. package/src/cli/prompts.ts +0 -64
  18. package/src/cli/response.ts +0 -44
  19. package/src/core/appium/client.ts +0 -248
  20. package/src/core/index.ts +0 -5
  21. package/src/core/selectors/generate-candidates.ts +0 -155
  22. package/src/core/selectors/score-candidates.ts +0 -184
  23. package/src/core/types.ts +0 -79
  24. package/src/core/xml/parse-source.ts +0 -197
  25. package/src/index.ts +0 -7
  26. package/src/server/appium-client.ts +0 -24
  27. package/src/server/index.ts +0 -6
  28. package/src/server/interaction-recorder.ts +0 -74
  29. package/src/server/proxy-middleware.ts +0 -68
  30. package/src/server/routes.ts +0 -53
  31. package/src/server/server.ts +0 -43
  32. package/src/server/types.ts +0 -34
  33. package/src/ui/bun.lock +0 -311
  34. package/src/ui/package.json +0 -20
  35. package/src/ui/src/App.css +0 -12
  36. package/src/ui/src/App.tsx +0 -41
  37. package/src/ui/src/components/ActionCarousel.css +0 -128
  38. package/src/ui/src/components/ActionCarousel.tsx +0 -92
  39. package/src/ui/src/components/Inspector.css +0 -314
  40. package/src/ui/src/components/Inspector.tsx +0 -265
  41. package/src/ui/src/components/InteractionCard.css +0 -159
  42. package/src/ui/src/components/InteractionCard.tsx +0 -60
  43. package/src/ui/src/components/MainInspector.css +0 -304
  44. package/src/ui/src/components/MainInspector.tsx +0 -304
  45. package/src/ui/src/components/Stats.css +0 -27
  46. package/src/ui/src/components/Timeline.css +0 -31
  47. package/src/ui/src/components/Timeline.tsx +0 -37
  48. package/src/ui/src/hooks/useInteractions.ts +0 -73
  49. package/src/ui/src/index.tsx +0 -11
  50. package/src/ui/src/services/api.ts +0 -41
  51. package/src/ui/src/styles/tokens.css +0 -126
  52. package/src/ui/src/types.ts +0 -34
  53. package/src/ui/src/utils/__tests__/locators.test.ts +0 -304
  54. package/src/ui/src/utils/__tests__/xml-parser.test.ts +0 -326
  55. package/src/ui/src/utils/locators.ts +0 -14
  56. package/src/ui/src/utils/xml-parser.ts +0 -45
  57. package/src/ui/tsconfig.json +0 -34
  58. package/src/ui/tsconfig.node.json +0 -11
  59. package/src/ui/vite.config.ts +0 -22
  60. package/tests/cli/arg-parser.test.ts +0 -397
  61. package/tests/cli/drive-commands.test.ts +0 -151
  62. package/tests/cli/selectors-best.test.ts +0 -42
  63. package/tests/cli/session-commands.test.ts +0 -53
  64. package/tests/core/selector-candidates.test.ts +0 -83
  65. package/tests/core/selector-scoring.test.ts +0 -75
  66. package/tests/core/xml-parser.test.ts +0 -56
  67. package/tests/server/appium-client.test.ts +0 -229
  68. package/tests/server/interaction-recorder.test.ts +0 -377
  69. package/tests/server/proxy-middleware.test.ts +0 -343
  70. package/tests/server/routes.test.ts +0 -305
  71. package/tsconfig.json +0 -26
  72. package/vitest.config.ts +0 -16
  73. package/vitest.ui.config.ts +0 -15
  74. package/workflow.gif +0 -0
@@ -1,20 +0,0 @@
1
- {
2
- "name": "appium-recorder-ui",
3
- "version": "2.0.0",
4
- "private": true,
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build",
9
- "preview": "vite preview"
10
- },
11
- "dependencies": {
12
- "@kobalte/core": "^0.13.11",
13
- "solid-js": "^1.9.11"
14
- },
15
- "devDependencies": {
16
- "typescript": "^5.9.3",
17
- "vite": "^6.3.5",
18
- "vite-plugin-solid": "^2.11.10"
19
- }
20
- }
@@ -1,12 +0,0 @@
1
- .app {
2
- height: 100vh;
3
- display: flex;
4
- flex-direction: column;
5
- background: var(--color-bg-primary);
6
- overflow: hidden;
7
- }
8
-
9
- .app-main {
10
- flex: 1;
11
- overflow: hidden;
12
- }
@@ -1,41 +0,0 @@
1
- import { type Component, createSignal, createMemo, createEffect } from 'solid-js';
2
- import { useInteractions } from './hooks/useInteractions';
3
- import { ActionCarousel } from './components/ActionCarousel';
4
- import { MainInspector } from './components/MainInspector';
5
- import './App.css';
6
-
7
- const App: Component = () => {
8
- const { interactions } = useInteractions();
9
- const [currentIndex, setCurrentIndex] = createSignal(0);
10
-
11
- // Filter to only actions (interactions with screenshots)
12
- const actions = createMemo(() =>
13
- interactions().filter(i => i.screenshot)
14
- );
15
-
16
- // Get the current action
17
- const currentAction = createMemo(() => actions()[currentIndex()]);
18
-
19
- // Auto-select the latest action when new ones are added
20
- createEffect(() => {
21
- const actionsCount = actions().length;
22
- if (actionsCount > 0) {
23
- setCurrentIndex(actionsCount - 1);
24
- }
25
- });
26
-
27
- return (
28
- <div class="app">
29
- <ActionCarousel
30
- interactions={interactions()}
31
- currentIndex={currentIndex()}
32
- onNavigate={setCurrentIndex}
33
- />
34
- <main class="app-main">
35
- <MainInspector interaction={currentAction()} />
36
- </main>
37
- </div>
38
- );
39
- };
40
-
41
- export default App;
@@ -1,128 +0,0 @@
1
- .carousel {
2
- display: flex;
3
- align-items: center;
4
- gap: var(--spacing-4);
5
- padding: var(--spacing-3) var(--spacing-6);
6
- background: var(--color-bg-secondary);
7
- border-bottom: 1px solid var(--color-border);
8
- }
9
-
10
- .carousel-empty {
11
- flex: 1;
12
- display: flex;
13
- align-items: center;
14
- justify-content: center;
15
- gap: var(--spacing-3);
16
- color: var(--color-text-tertiary);
17
- font-size: var(--font-size-sm);
18
- padding: var(--spacing-2) 0;
19
- }
20
-
21
- .carousel-empty-icon {
22
- font-size: var(--font-size-lg);
23
- }
24
-
25
- .carousel-btn {
26
- background: var(--color-bg-secondary);
27
- color: var(--color-text-secondary);
28
- border: 1px solid var(--color-border);
29
- padding: var(--spacing-2) var(--spacing-4);
30
- border-radius: var(--radius-lg);
31
- cursor: pointer;
32
- font-weight: var(--font-weight-medium);
33
- font-size: var(--font-size-sm);
34
- transition: all var(--transition-fast);
35
- white-space: nowrap;
36
- }
37
-
38
- .carousel-btn:hover:not(:disabled) {
39
- background: var(--color-accent-primary);
40
- color: white;
41
- border-color: var(--color-accent-primary);
42
- }
43
-
44
- .carousel-btn:disabled {
45
- opacity: 0.35;
46
- cursor: not-allowed;
47
- }
48
-
49
- .carousel-info {
50
- flex: 1;
51
- display: flex;
52
- flex-direction: column;
53
- align-items: center;
54
- gap: var(--spacing-1);
55
- }
56
-
57
- .carousel-counter {
58
- font-size: var(--font-size-xs);
59
- color: var(--color-text-tertiary);
60
- font-weight: var(--font-weight-medium);
61
- letter-spacing: 0.02em;
62
- }
63
-
64
- .carousel-details {
65
- display: flex;
66
- align-items: center;
67
- gap: var(--spacing-3);
68
- flex-wrap: wrap;
69
- justify-content: center;
70
- }
71
-
72
- .carousel-id {
73
- font-size: var(--font-size-xs);
74
- color: var(--color-text-tertiary);
75
- font-weight: var(--font-weight-medium);
76
- }
77
-
78
- .carousel-method {
79
- font-size: var(--font-size-xs);
80
- font-weight: var(--font-weight-semibold);
81
- padding: 2px var(--spacing-2);
82
- border-radius: var(--radius-sm);
83
- text-transform: uppercase;
84
- letter-spacing: 0.03em;
85
- }
86
-
87
- .carousel-method.POST {
88
- background: #E8F5EE;
89
- color: var(--color-accent-success);
90
- }
91
-
92
- .carousel-method.GET {
93
- background: #FFF3E0;
94
- color: var(--color-accent-warning);
95
- }
96
-
97
- .carousel-method.DELETE {
98
- background: #FDECEB;
99
- color: var(--color-accent-error);
100
- }
101
-
102
- .carousel-path {
103
- font-size: var(--font-size-sm);
104
- color: var(--color-text-secondary);
105
- max-width: 400px;
106
- overflow: hidden;
107
- text-overflow: ellipsis;
108
- white-space: nowrap;
109
- }
110
-
111
- .carousel-time {
112
- font-size: var(--font-size-xs);
113
- color: var(--color-text-tertiary);
114
- }
115
-
116
- .carousel-element {
117
- font-size: var(--font-size-xs);
118
- color: var(--color-text-secondary);
119
- }
120
-
121
- .carousel-element-using {
122
- color: var(--color-accent-secondary);
123
- font-weight: var(--font-weight-medium);
124
- }
125
-
126
- .carousel-element-value {
127
- color: var(--color-accent-primary);
128
- }
@@ -1,92 +0,0 @@
1
- import { type Component, Show, createMemo } from 'solid-js';
2
- import type { Interaction } from '../types';
3
- import './ActionCarousel.css';
4
-
5
- type ActionCarouselProps = {
6
- interactions: Interaction[];
7
- currentIndex: number;
8
- onNavigate: (index: number) => void;
9
- };
10
-
11
- export const ActionCarousel: Component<ActionCarouselProps> = (props) => {
12
- const actions = createMemo(() =>
13
- props.interactions.filter(i => i.screenshot)
14
- );
15
-
16
- const currentAction = createMemo(() => actions()[props.currentIndex]);
17
- const total = createMemo(() => actions().length);
18
-
19
- const goToPrevious = () => {
20
- if (props.currentIndex > 0) {
21
- props.onNavigate(props.currentIndex - 1);
22
- }
23
- };
24
-
25
- const goToNext = () => {
26
- if (props.currentIndex < total() - 1) {
27
- props.onNavigate(props.currentIndex + 1);
28
- }
29
- };
30
-
31
- const formattedTime = () => {
32
- const action = currentAction();
33
- return action ? new Date(action.timestamp).toLocaleTimeString() : '';
34
- };
35
-
36
- return (
37
- <div class="carousel">
38
- <Show
39
- when={total() > 0}
40
- fallback={
41
- <div class="carousel-empty">
42
- <span class="carousel-empty-icon">📱</span>
43
- <span>No actions recorded yet. Connect Appium Inspector to port 4724 and start interacting.</span>
44
- </div>
45
- }
46
- >
47
- <button
48
- class="carousel-btn"
49
- onClick={goToPrevious}
50
- disabled={props.currentIndex === 0}
51
- >
52
- ← Previous
53
- </button>
54
-
55
- <div class="carousel-info">
56
- <div class="carousel-counter">
57
- Action {props.currentIndex + 1} of {total()}
58
- </div>
59
- <Show when={currentAction()}>
60
- <div class="carousel-details">
61
- <span class="carousel-id">#{currentAction()!.id}</span>
62
- <span classList={{
63
- 'carousel-method': true,
64
- [currentAction()!.method]: true
65
- }}>
66
- {currentAction()!.method}
67
- </span>
68
- <span class="carousel-path">{currentAction()!.path}</span>
69
- <span class="carousel-time">{formattedTime()}</span>
70
- </div>
71
- <Show when={currentAction()!.elementInfo}>
72
- <div class="carousel-element">
73
- <span class="carousel-element-using">{currentAction()!.elementInfo!.using}:</span>
74
- {' "'}
75
- <span class="carousel-element-value">{currentAction()!.elementInfo!.value}</span>
76
- {'"'}
77
- </div>
78
- </Show>
79
- </Show>
80
- </div>
81
-
82
- <button
83
- class="carousel-btn"
84
- onClick={goToNext}
85
- disabled={props.currentIndex === total() - 1}
86
- >
87
- Next →
88
- </button>
89
- </Show>
90
- </div>
91
- );
92
- };
@@ -1,314 +0,0 @@
1
- .inspector-overlay {
2
- position: fixed;
3
- inset: 0;
4
- background: rgba(0, 0, 0, 0.4);
5
- backdrop-filter: blur(4px);
6
- z-index: var(--z-modal);
7
- animation: fadeIn var(--transition-base);
8
- }
9
-
10
- .inspector-modal {
11
- position: fixed;
12
- top: 50%;
13
- left: 50%;
14
- transform: translate(-50%, -50%);
15
- background: var(--color-bg-primary);
16
- border-radius: var(--radius-2xl);
17
- padding: var(--spacing-6);
18
- width: 90vw;
19
- max-width: 1400px;
20
- max-height: 90vh;
21
- overflow: hidden;
22
- z-index: var(--z-modal);
23
- box-shadow: var(--shadow-lg);
24
- border: 1px solid var(--color-border);
25
- animation: slideIn var(--transition-base);
26
- }
27
-
28
- .inspector-close {
29
- position: absolute;
30
- top: var(--spacing-4);
31
- right: var(--spacing-4);
32
- background: var(--color-bg-tertiary);
33
- border: 1px solid var(--color-border);
34
- width: 36px;
35
- height: 36px;
36
- border-radius: var(--radius-full);
37
- font-size: var(--font-size-base);
38
- cursor: pointer;
39
- color: var(--color-text-secondary);
40
- transition: all var(--transition-fast);
41
- z-index: 1;
42
- display: flex;
43
- align-items: center;
44
- justify-content: center;
45
- }
46
-
47
- .inspector-close:hover {
48
- background: var(--color-accent-error);
49
- border-color: var(--color-accent-error);
50
- color: white;
51
- }
52
-
53
- .inspector-panel {
54
- display: flex;
55
- gap: var(--spacing-6);
56
- height: 100%;
57
- }
58
-
59
- .inspector-left {
60
- flex-shrink: 0;
61
- }
62
-
63
- .inspector-screenshot {
64
- max-height: 80vh;
65
- border-radius: var(--radius-xl);
66
- box-shadow: var(--shadow-md);
67
- border: 1px solid var(--color-border);
68
- }
69
-
70
- .inspector-right {
71
- flex: 1;
72
- overflow-y: auto;
73
- padding-right: var(--spacing-2);
74
- max-height: calc(90vh - var(--spacing-12));
75
- }
76
-
77
- .inspector-section {
78
- background: var(--color-bg-secondary);
79
- border-radius: var(--radius-xl);
80
- padding: var(--spacing-5);
81
- margin-bottom: var(--spacing-3);
82
- border: 1px solid var(--color-border);
83
- }
84
-
85
- .inspector-section h3 {
86
- color: var(--color-text-primary);
87
- font-size: var(--font-size-sm);
88
- margin-bottom: var(--spacing-3);
89
- font-weight: var(--font-weight-semibold);
90
- }
91
-
92
- .query-tester {
93
- display: flex;
94
- flex-direction: column;
95
- gap: var(--spacing-3);
96
- }
97
-
98
- .query-row {
99
- display: flex;
100
- gap: var(--spacing-2);
101
- }
102
-
103
- .query-select,
104
- .query-input {
105
- background: var(--color-bg-primary);
106
- color: var(--color-text-primary);
107
- border: 1px solid var(--color-border);
108
- padding: var(--spacing-2) var(--spacing-3);
109
- border-radius: var(--radius-lg);
110
- font-size: var(--font-size-sm);
111
- font-family: var(--font-family);
112
- transition: border-color var(--transition-fast);
113
- }
114
-
115
- .query-select:focus,
116
- .query-input:focus {
117
- outline: none;
118
- border-color: var(--color-accent-primary);
119
- }
120
-
121
- .query-select {
122
- min-width: 150px;
123
- }
124
-
125
- .query-input {
126
- flex: 1;
127
- }
128
-
129
- .query-btn {
130
- background: var(--color-accent-primary);
131
- color: white;
132
- border: none;
133
- padding: var(--spacing-2) var(--spacing-5);
134
- border-radius: var(--radius-lg);
135
- cursor: pointer;
136
- font-weight: var(--font-weight-medium);
137
- font-size: var(--font-size-sm);
138
- transition: all var(--transition-fast);
139
- }
140
-
141
- .query-btn:hover {
142
- background: var(--color-accent-secondary);
143
- }
144
-
145
- .query-result {
146
- padding: var(--spacing-2) var(--spacing-3);
147
- border-radius: var(--radius-md);
148
- font-size: var(--font-size-sm);
149
- }
150
-
151
- .query-result.success {
152
- background: #E8F5EE;
153
- color: var(--color-accent-success);
154
- }
155
-
156
- .element-details {
157
- display: flex;
158
- flex-direction: column;
159
- gap: var(--spacing-2);
160
- }
161
-
162
- .element-attr {
163
- display: flex;
164
- gap: var(--spacing-4);
165
- font-size: var(--font-size-sm);
166
- }
167
-
168
- .attr-name {
169
- color: var(--color-text-tertiary);
170
- min-width: 90px;
171
- font-weight: var(--font-weight-medium);
172
- }
173
-
174
- .attr-value {
175
- color: var(--color-text-primary);
176
- }
177
-
178
- .locators-list {
179
- display: flex;
180
- flex-direction: column;
181
- gap: var(--spacing-1);
182
- }
183
-
184
- .locator-row {
185
- display: flex;
186
- gap: var(--spacing-3);
187
- padding: var(--spacing-2) var(--spacing-3);
188
- background: var(--color-bg-primary);
189
- border-radius: var(--radius-md);
190
- cursor: pointer;
191
- transition: all var(--transition-fast);
192
- border: 1px solid transparent;
193
- }
194
-
195
- .locator-row:hover {
196
- background: var(--color-bg-tertiary);
197
- border-color: var(--color-border);
198
- }
199
-
200
- .locator-strategy {
201
- color: var(--color-accent-primary);
202
- font-size: var(--font-size-xs);
203
- min-width: 140px;
204
- font-weight: var(--font-weight-medium);
205
- font-family: var(--font-mono);
206
- }
207
-
208
- .locator-value {
209
- flex: 1;
210
- font-size: var(--font-size-xs);
211
- color: var(--color-text-secondary);
212
- word-break: break-all;
213
- font-family: var(--font-mono);
214
- }
215
-
216
- @keyframes fadeIn {
217
- from { opacity: 0; }
218
- to { opacity: 1; }
219
- }
220
-
221
- @keyframes slideIn {
222
- from {
223
- opacity: 0;
224
- transform: translate(-50%, -48%);
225
- }
226
- to {
227
- opacity: 1;
228
- transform: translate(-50%, -50%);
229
- }
230
- }
231
-
232
- .element-item {
233
- display: flex;
234
- gap: var(--spacing-3);
235
- padding: var(--spacing-2) var(--spacing-3);
236
- background: var(--color-bg-primary);
237
- border-radius: var(--radius-sm);
238
- cursor: pointer;
239
- transition: all var(--transition-fast);
240
- font-size: var(--font-size-xs);
241
- border: 1px solid transparent;
242
- }
243
-
244
- .element-item:hover {
245
- background: var(--color-bg-tertiary);
246
- border-color: var(--color-border);
247
- }
248
-
249
- .element-item.selected {
250
- background: var(--color-accent-primary);
251
- color: white;
252
- border-color: var(--color-accent-primary);
253
- }
254
-
255
- .element-item.selected .element-type {
256
- color: white;
257
- }
258
-
259
- .element-type {
260
- color: var(--color-accent-primary);
261
- font-weight: var(--font-weight-medium);
262
- min-width: 100px;
263
- }
264
-
265
- .element-name {
266
- color: var(--color-text-secondary);
267
- overflow: hidden;
268
- text-overflow: ellipsis;
269
- white-space: nowrap;
270
- }
271
-
272
- .element-item.selected .element-name {
273
- color: rgba(255, 255, 255, 0.85);
274
- }
275
-
276
- .elements-more {
277
- padding: var(--spacing-2);
278
- color: var(--color-text-tertiary);
279
- font-size: var(--font-size-xs);
280
- text-align: center;
281
- }
282
-
283
- /* XML Source */
284
- .source-toggle-btn {
285
- background: var(--color-bg-tertiary);
286
- color: var(--color-text-secondary);
287
- border: 1px solid var(--color-border);
288
- padding: var(--spacing-2) var(--spacing-4);
289
- border-radius: var(--radius-lg);
290
- cursor: pointer;
291
- font-size: var(--font-size-sm);
292
- transition: all var(--transition-fast);
293
- margin-bottom: var(--spacing-3);
294
- }
295
-
296
- .source-toggle-btn:hover {
297
- background: var(--color-border);
298
- color: var(--color-text-primary);
299
- }
300
-
301
- .xml-source {
302
- background: var(--color-bg-primary);
303
- padding: var(--spacing-4);
304
- border-radius: var(--radius-lg);
305
- font-size: var(--font-size-xs);
306
- font-family: var(--font-mono);
307
- max-height: 300px;
308
- overflow: auto;
309
- white-space: pre-wrap;
310
- word-break: break-all;
311
- color: var(--color-text-secondary);
312
- border: 1px solid var(--color-border);
313
- line-height: 1.7;
314
- }