lwc-convert 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 (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +719 -0
  3. package/dist/cli/commands/aura.d.ts +6 -0
  4. package/dist/cli/commands/aura.d.ts.map +1 -0
  5. package/dist/cli/commands/aura.js +225 -0
  6. package/dist/cli/commands/aura.js.map +1 -0
  7. package/dist/cli/commands/vf.d.ts +6 -0
  8. package/dist/cli/commands/vf.d.ts.map +1 -0
  9. package/dist/cli/commands/vf.js +218 -0
  10. package/dist/cli/commands/vf.js.map +1 -0
  11. package/dist/cli/interactive.d.ts +20 -0
  12. package/dist/cli/interactive.d.ts.map +1 -0
  13. package/dist/cli/interactive.js +577 -0
  14. package/dist/cli/interactive.js.map +1 -0
  15. package/dist/cli/options.d.ts +21 -0
  16. package/dist/cli/options.d.ts.map +1 -0
  17. package/dist/cli/options.js +24 -0
  18. package/dist/cli/options.js.map +1 -0
  19. package/dist/generators/full-conversion.d.ts +41 -0
  20. package/dist/generators/full-conversion.d.ts.map +1 -0
  21. package/dist/generators/full-conversion.js +538 -0
  22. package/dist/generators/full-conversion.js.map +1 -0
  23. package/dist/generators/scaffolding.d.ts +40 -0
  24. package/dist/generators/scaffolding.d.ts.map +1 -0
  25. package/dist/generators/scaffolding.js +716 -0
  26. package/dist/generators/scaffolding.js.map +1 -0
  27. package/dist/generators/test-comparison.d.ts +47 -0
  28. package/dist/generators/test-comparison.d.ts.map +1 -0
  29. package/dist/generators/test-comparison.js +855 -0
  30. package/dist/generators/test-comparison.js.map +1 -0
  31. package/dist/generators/test-generator.d.ts +27 -0
  32. package/dist/generators/test-generator.d.ts.map +1 -0
  33. package/dist/generators/test-generator.js +385 -0
  34. package/dist/generators/test-generator.js.map +1 -0
  35. package/dist/index.d.ts +6 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +226 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/mappings/aura-to-lwc.json +321 -0
  40. package/dist/mappings/vf-to-lwc.json +354 -0
  41. package/dist/parsers/aura/controller-parser.d.ts +36 -0
  42. package/dist/parsers/aura/controller-parser.d.ts.map +1 -0
  43. package/dist/parsers/aura/controller-parser.js +269 -0
  44. package/dist/parsers/aura/controller-parser.js.map +1 -0
  45. package/dist/parsers/aura/helper-parser.d.ts +21 -0
  46. package/dist/parsers/aura/helper-parser.d.ts.map +1 -0
  47. package/dist/parsers/aura/helper-parser.js +173 -0
  48. package/dist/parsers/aura/helper-parser.js.map +1 -0
  49. package/dist/parsers/aura/markup-parser.d.ts +59 -0
  50. package/dist/parsers/aura/markup-parser.d.ts.map +1 -0
  51. package/dist/parsers/aura/markup-parser.js +279 -0
  52. package/dist/parsers/aura/markup-parser.js.map +1 -0
  53. package/dist/parsers/aura/style-parser.d.ts +37 -0
  54. package/dist/parsers/aura/style-parser.d.ts.map +1 -0
  55. package/dist/parsers/aura/style-parser.js +151 -0
  56. package/dist/parsers/aura/style-parser.js.map +1 -0
  57. package/dist/parsers/vf/apex-parser.d.ts +51 -0
  58. package/dist/parsers/vf/apex-parser.d.ts.map +1 -0
  59. package/dist/parsers/vf/apex-parser.js +251 -0
  60. package/dist/parsers/vf/apex-parser.js.map +1 -0
  61. package/dist/parsers/vf/page-parser.d.ts +61 -0
  62. package/dist/parsers/vf/page-parser.d.ts.map +1 -0
  63. package/dist/parsers/vf/page-parser.js +403 -0
  64. package/dist/parsers/vf/page-parser.js.map +1 -0
  65. package/dist/transformers/aura-to-lwc/controller.d.ts +36 -0
  66. package/dist/transformers/aura-to-lwc/controller.d.ts.map +1 -0
  67. package/dist/transformers/aura-to-lwc/controller.js +372 -0
  68. package/dist/transformers/aura-to-lwc/controller.js.map +1 -0
  69. package/dist/transformers/aura-to-lwc/events.d.ts +47 -0
  70. package/dist/transformers/aura-to-lwc/events.d.ts.map +1 -0
  71. package/dist/transformers/aura-to-lwc/events.js +262 -0
  72. package/dist/transformers/aura-to-lwc/events.js.map +1 -0
  73. package/dist/transformers/aura-to-lwc/markup.d.ts +51 -0
  74. package/dist/transformers/aura-to-lwc/markup.d.ts.map +1 -0
  75. package/dist/transformers/aura-to-lwc/markup.js +465 -0
  76. package/dist/transformers/aura-to-lwc/markup.js.map +1 -0
  77. package/dist/transformers/vf-to-lwc/components.d.ts +40 -0
  78. package/dist/transformers/vf-to-lwc/components.d.ts.map +1 -0
  79. package/dist/transformers/vf-to-lwc/components.js +374 -0
  80. package/dist/transformers/vf-to-lwc/components.js.map +1 -0
  81. package/dist/transformers/vf-to-lwc/data-binding.d.ts +53 -0
  82. package/dist/transformers/vf-to-lwc/data-binding.d.ts.map +1 -0
  83. package/dist/transformers/vf-to-lwc/data-binding.js +660 -0
  84. package/dist/transformers/vf-to-lwc/data-binding.js.map +1 -0
  85. package/dist/transformers/vf-to-lwc/markup.d.ts +44 -0
  86. package/dist/transformers/vf-to-lwc/markup.d.ts.map +1 -0
  87. package/dist/transformers/vf-to-lwc/markup.js +816 -0
  88. package/dist/transformers/vf-to-lwc/markup.js.map +1 -0
  89. package/dist/utils/confidence-scorer.d.ts +100 -0
  90. package/dist/utils/confidence-scorer.d.ts.map +1 -0
  91. package/dist/utils/confidence-scorer.js +358 -0
  92. package/dist/utils/confidence-scorer.js.map +1 -0
  93. package/dist/utils/file-io.d.ts +62 -0
  94. package/dist/utils/file-io.d.ts.map +1 -0
  95. package/dist/utils/file-io.js +248 -0
  96. package/dist/utils/file-io.js.map +1 -0
  97. package/dist/utils/logger.d.ts +34 -0
  98. package/dist/utils/logger.d.ts.map +1 -0
  99. package/dist/utils/logger.js +130 -0
  100. package/dist/utils/logger.js.map +1 -0
  101. package/dist/utils/open-folder.d.ts +9 -0
  102. package/dist/utils/open-folder.d.ts.map +1 -0
  103. package/dist/utils/open-folder.js +76 -0
  104. package/dist/utils/open-folder.js.map +1 -0
  105. package/dist/utils/path-resolver.d.ts +29 -0
  106. package/dist/utils/path-resolver.d.ts.map +1 -0
  107. package/dist/utils/path-resolver.js +240 -0
  108. package/dist/utils/path-resolver.js.map +1 -0
  109. package/dist/utils/session-store.d.ts +158 -0
  110. package/dist/utils/session-store.d.ts.map +1 -0
  111. package/dist/utils/session-store.js +518 -0
  112. package/dist/utils/session-store.js.map +1 -0
  113. package/dist/utils/vf-controller-resolver.d.ts +36 -0
  114. package/dist/utils/vf-controller-resolver.d.ts.map +1 -0
  115. package/dist/utils/vf-controller-resolver.js +162 -0
  116. package/dist/utils/vf-controller-resolver.js.map +1 -0
  117. package/package.json +81 -0
@@ -0,0 +1,855 @@
1
+ "use strict";
2
+ /**
3
+ * Test Comparison Generator
4
+ * Creates before/after tests for conversion verification
5
+ *
6
+ * Before Tests: Document the expected behaviors of the original Aura component
7
+ * After Tests: Verify the converted LWC preserves those behaviors
8
+ * Comparison: Run both and compare outputs
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.generateTestComparison = generateTestComparison;
12
+ const file_io_1 = require("../utils/file-io");
13
+ /**
14
+ * Extract behaviors from Aura component and generate comparison tests
15
+ */
16
+ function generateTestComparison(markup, transformedMarkup, controller, helper) {
17
+ const lwcName = (0, file_io_1.toLwcName)(markup.componentName);
18
+ const className = (0, file_io_1.toPascalCase)(markup.componentName);
19
+ const behaviorTests = [];
20
+ let testId = 1;
21
+ // 1. Lifecycle behaviors
22
+ const lifecycleTests = extractLifecycleBehaviors(markup, controller, helper, testId);
23
+ behaviorTests.push(...lifecycleTests);
24
+ testId += lifecycleTests.length;
25
+ // 2. Data binding behaviors
26
+ const dataTests = extractDataBindingBehaviors(markup, transformedMarkup, controller, testId);
27
+ behaviorTests.push(...dataTests);
28
+ testId += dataTests.length;
29
+ // 3. Event handling behaviors
30
+ const eventTests = extractEventBehaviors(markup, controller, helper, testId);
31
+ behaviorTests.push(...eventTests);
32
+ testId += eventTests.length;
33
+ // 4. UI rendering behaviors
34
+ const uiTests = extractUIBehaviors(markup, transformedMarkup, testId);
35
+ behaviorTests.push(...uiTests);
36
+ testId += uiTests.length;
37
+ // 5. LMS behaviors
38
+ const lmsTests = extractLMSBehaviors(transformedMarkup, controller, testId);
39
+ behaviorTests.push(...lmsTests);
40
+ testId += lmsTests.length;
41
+ // 6. Apex/server call behaviors
42
+ const apexTests = extractApexBehaviors(markup, controller, helper, testId);
43
+ behaviorTests.push(...apexTests);
44
+ // Generate test files
45
+ const beforeTestFile = generateBeforeTestFile(markup.componentName, behaviorTests);
46
+ const afterTestFile = generateAfterTestFile(lwcName, className, behaviorTests, transformedMarkup);
47
+ const comparisonReport = generateComparisonReport(markup.componentName, lwcName, behaviorTests);
48
+ return {
49
+ componentName: markup.componentName,
50
+ lwcName,
51
+ behaviorTests,
52
+ beforeTestFile,
53
+ afterTestFile,
54
+ comparisonReport,
55
+ };
56
+ }
57
+ /**
58
+ * Extract lifecycle-related behaviors (init, render, destroy)
59
+ */
60
+ function extractLifecycleBehaviors(markup, controller, helper, startId = 1) {
61
+ const tests = [];
62
+ let id = startId;
63
+ // Init handler
64
+ const initHandler = markup.handlers.find(h => h.name === 'init');
65
+ if (initHandler) {
66
+ const funcName = initHandler.action.replace('{!c.', '').replace('}', '');
67
+ const initFunc = controller?.functions.find(f => f.name === funcName);
68
+ // Analyze what init does
69
+ const setsAttributes = [];
70
+ const callsServer = initFunc?.serverCalls.length ? true : false;
71
+ const callsHelper = [];
72
+ if (initFunc) {
73
+ // Find component.set calls
74
+ const setMatches = initFunc.body.matchAll(/component\.set\s*\(\s*["']v\.(\w+)["']/g);
75
+ for (const match of setMatches) {
76
+ setsAttributes.push(match[1]);
77
+ }
78
+ // Find helper calls
79
+ const helperMatches = initFunc.body.matchAll(/helper\.(\w+)\s*\(/g);
80
+ for (const match of helperMatches) {
81
+ callsHelper.push(match[1]);
82
+ }
83
+ }
84
+ tests.push({
85
+ id: `behavior-${id++}`,
86
+ category: 'lifecycle',
87
+ name: 'Component Initialization',
88
+ description: `When component initializes, ${funcName} is called`,
89
+ auraBehavior: {
90
+ pattern: `aura:handler name="init" action="{!c.${funcName}}"`,
91
+ code: initFunc?.body,
92
+ attributes: { handler: funcName },
93
+ },
94
+ lwcBehavior: {
95
+ pattern: 'connectedCallback()',
96
+ properties: { lifecycle: 'connectedCallback' },
97
+ },
98
+ testCode: {
99
+ before: generateInitBeforeTest(funcName, setsAttributes, callsServer, callsHelper),
100
+ after: generateInitAfterTest(setsAttributes, callsServer),
101
+ },
102
+ });
103
+ // If init sets specific attributes, add individual tests
104
+ for (const attr of setsAttributes) {
105
+ tests.push({
106
+ id: `behavior-${id++}`,
107
+ category: 'lifecycle',
108
+ name: `Init sets ${attr}`,
109
+ description: `On init, ${attr} property is set`,
110
+ auraBehavior: {
111
+ pattern: `component.set("v.${attr}", ...)`,
112
+ },
113
+ lwcBehavior: {
114
+ pattern: `this.${attr} = ...`,
115
+ },
116
+ testCode: {
117
+ before: `// Aura: After init, v.${attr} should be set\nexpect(component.get("v.${attr}")).toBeDefined();`,
118
+ after: `test('should set ${attr} on init', async () => {\n document.body.appendChild(element);\n await Promise.resolve();\n // LWC: After connectedCallback, ${attr} should be set\n expect(element.${attr}).toBeDefined();\n});`,
119
+ },
120
+ });
121
+ }
122
+ }
123
+ // Render handler
124
+ const renderHandler = markup.handlers.find(h => h.name === 'render' || h.name === 'afterRender');
125
+ if (renderHandler) {
126
+ tests.push({
127
+ id: `behavior-${id++}`,
128
+ category: 'lifecycle',
129
+ name: 'After Render Callback',
130
+ description: 'Code runs after component renders',
131
+ auraBehavior: {
132
+ pattern: `aura:handler name="${renderHandler.name}" action="${renderHandler.action}"`,
133
+ },
134
+ lwcBehavior: {
135
+ pattern: 'renderedCallback()',
136
+ },
137
+ testCode: {
138
+ before: `// Aura: ${renderHandler.name} handler is called after render`,
139
+ after: `// LWC: renderedCallback is called after each render\n// Note: Add isRendered flag to prevent repeated execution`,
140
+ },
141
+ });
142
+ }
143
+ // Destroy handler
144
+ const destroyHandler = markup.handlers.find(h => h.name === 'destroy');
145
+ if (destroyHandler) {
146
+ tests.push({
147
+ id: `behavior-${id++}`,
148
+ category: 'lifecycle',
149
+ name: 'Component Cleanup',
150
+ description: 'Cleanup runs when component is destroyed',
151
+ auraBehavior: {
152
+ pattern: `aura:handler name="destroy" action="${destroyHandler.action}"`,
153
+ },
154
+ lwcBehavior: {
155
+ pattern: 'disconnectedCallback()',
156
+ },
157
+ testCode: {
158
+ before: `// Aura: destroy handler cleans up resources`,
159
+ after: `test('should cleanup on disconnect', () => {\n document.body.appendChild(element);\n document.body.removeChild(element);\n // Verify cleanup occurred\n});`,
160
+ },
161
+ });
162
+ }
163
+ return tests;
164
+ }
165
+ /**
166
+ * Extract data binding behaviors (attributes, getters)
167
+ */
168
+ function extractDataBindingBehaviors(markup, transformedMarkup, controller, startId = 1) {
169
+ const tests = [];
170
+ let id = startId;
171
+ // Public attributes
172
+ for (const attr of markup.attributes) {
173
+ const isPublic = !attr.access || attr.access === 'public' || attr.access === 'global';
174
+ if (isPublic) {
175
+ tests.push({
176
+ id: `behavior-${id++}`,
177
+ category: 'data',
178
+ name: `Public property: ${attr.name}`,
179
+ description: `${attr.name} is exposed as a public property`,
180
+ auraBehavior: {
181
+ pattern: `<aura:attribute name="${attr.name}" type="${attr.type}" access="${attr.access || 'public'}"/>`,
182
+ attributes: {
183
+ name: attr.name,
184
+ type: attr.type,
185
+ default: attr.default || '',
186
+ },
187
+ },
188
+ lwcBehavior: {
189
+ pattern: `@api ${attr.name}`,
190
+ properties: {
191
+ decorator: '@api',
192
+ type: attr.type,
193
+ },
194
+ },
195
+ testCode: {
196
+ before: `// Aura: v.${attr.name} is accessible from parent\nexpect(component.get("v.${attr.name}")).toBeDefined();`,
197
+ after: generatePropertyAfterTest(attr.name, attr.type, attr.default),
198
+ },
199
+ });
200
+ }
201
+ }
202
+ // force:recordData bindings
203
+ for (const rds of transformedMarkup.recordDataServices) {
204
+ tests.push({
205
+ id: `behavior-${id++}`,
206
+ category: 'data',
207
+ name: `Wire record data: ${rds.auraId}`,
208
+ description: `Record data loaded via ${rds.recordIdBinding}`,
209
+ auraBehavior: {
210
+ pattern: `<force:recordData aura:id="${rds.auraId}" recordId="{!v.${rds.recordIdBinding}}" fields="[${rds.fields.join(', ')}]"/>`,
211
+ attributes: {
212
+ recordIdBinding: rds.recordIdBinding,
213
+ fields: rds.fields.join(', '),
214
+ },
215
+ },
216
+ lwcBehavior: {
217
+ pattern: `@wire(getRecord, { recordId: '$${rds.recordIdBinding}', fields: [${rds.fields.map(f => `'${f}'`).join(', ')}] })`,
218
+ },
219
+ testCode: {
220
+ before: `// Aura: force:recordData loads record when recordId changes`,
221
+ after: generateWireAfterTest(rds.recordIdBinding, rds.fields),
222
+ },
223
+ });
224
+ }
225
+ return tests;
226
+ }
227
+ /**
228
+ * Extract event handling behaviors
229
+ */
230
+ function extractEventBehaviors(markup, controller, helper, startId = 1) {
231
+ const tests = [];
232
+ let id = startId;
233
+ // Registered events (component events)
234
+ for (const event of markup.registeredEvents) {
235
+ tests.push({
236
+ id: `behavior-${id++}`,
237
+ category: 'event',
238
+ name: `Fires event: ${event.name}`,
239
+ description: `Component can fire ${event.name} event`,
240
+ auraBehavior: {
241
+ pattern: `<aura:registerEvent name="${event.name}" type="${event.type}"/>`,
242
+ code: `component.getEvent("${event.name}").fire()`,
243
+ },
244
+ lwcBehavior: {
245
+ pattern: `this.dispatchEvent(new CustomEvent('${event.name.toLowerCase()}'))`,
246
+ },
247
+ testCode: {
248
+ before: `// Aura: Component fires ${event.name} event\nconst evt = component.getEvent("${event.name}");\nevt.fire();`,
249
+ after: generateEventAfterTest(event.name),
250
+ },
251
+ });
252
+ }
253
+ // Event handlers from controller
254
+ if (controller) {
255
+ for (const func of controller.functions) {
256
+ // Skip init handler (already covered in lifecycle)
257
+ if (func.name === 'doInit')
258
+ continue;
259
+ // Check if it's an event handler (starts with handle, on, or is called from markup)
260
+ if (func.name.startsWith('handle') || func.name.startsWith('on')) {
261
+ const eventName = func.name.replace(/^(handle|on)/, '').toLowerCase();
262
+ tests.push({
263
+ id: `behavior-${id++}`,
264
+ category: 'event',
265
+ name: `Handle: ${func.name}`,
266
+ description: `${func.name} handles user interaction`,
267
+ auraBehavior: {
268
+ pattern: `{!c.${func.name}}`,
269
+ code: func.body,
270
+ },
271
+ lwcBehavior: {
272
+ pattern: `${func.name}(event)`,
273
+ },
274
+ testCode: {
275
+ before: `// Aura: ${func.name} is called on ${eventName} event`,
276
+ after: generateHandlerAfterTest(func.name, eventName, func),
277
+ },
278
+ });
279
+ }
280
+ }
281
+ }
282
+ return tests;
283
+ }
284
+ /**
285
+ * Extract UI rendering behaviors
286
+ */
287
+ function extractUIBehaviors(markup, transformedMarkup, startId = 1) {
288
+ const tests = [];
289
+ let id = startId;
290
+ // Conditional rendering (aura:if)
291
+ const conditionalExpressions = markup.expressions.filter(e => e.original.includes('isTrue') || e.original.includes('isFalse'));
292
+ for (const component of transformedMarkup.usedComponents) {
293
+ tests.push({
294
+ id: `behavior-${id++}`,
295
+ category: 'ui',
296
+ name: `Renders ${component}`,
297
+ description: `Component includes ${component}`,
298
+ auraBehavior: {
299
+ pattern: `<${component.replace('lightning-', 'lightning:')}>`,
300
+ },
301
+ lwcBehavior: {
302
+ pattern: `<${component}>`,
303
+ },
304
+ testCode: {
305
+ before: `// Aura: ${component.replace('lightning-', 'lightning:')} is rendered`,
306
+ after: `test('should render ${component}', () => {\n document.body.appendChild(element);\n const comp = element.shadowRoot.querySelector('${component}');\n expect(comp).toBeTruthy();\n});`,
307
+ },
308
+ });
309
+ }
310
+ // Iteration rendering
311
+ if (transformedMarkup.usedDirectives.includes('for:each')) {
312
+ tests.push({
313
+ id: `behavior-${id++}`,
314
+ category: 'ui',
315
+ name: 'List rendering',
316
+ description: 'Component renders a list of items',
317
+ auraBehavior: {
318
+ pattern: '<aura:iteration items="{!v.items}" var="item">',
319
+ },
320
+ lwcBehavior: {
321
+ pattern: '<template for:each={items} for:item="item">',
322
+ },
323
+ testCode: {
324
+ before: `// Aura: aura:iteration renders list items`,
325
+ after: `test('should render list items', async () => {\n element.items = [{Id: '1', Name: 'Test'}];\n document.body.appendChild(element);\n await Promise.resolve();\n // Verify items rendered\n});`,
326
+ },
327
+ });
328
+ }
329
+ // Conditional rendering
330
+ if (transformedMarkup.usedDirectives.includes('lwc:if')) {
331
+ tests.push({
332
+ id: `behavior-${id++}`,
333
+ category: 'ui',
334
+ name: 'Conditional rendering',
335
+ description: 'Component conditionally shows/hides content',
336
+ auraBehavior: {
337
+ pattern: '<aura:if isTrue="{!v.condition}">',
338
+ },
339
+ lwcBehavior: {
340
+ pattern: '<template lwc:if={condition}>',
341
+ },
342
+ testCode: {
343
+ before: `// Aura: Content shown/hidden based on condition`,
344
+ after: `test('should conditionally render content', async () => {\n element.condition = false;\n document.body.appendChild(element);\n await Promise.resolve();\n // Verify content hidden\n \n element.condition = true;\n await Promise.resolve();\n // Verify content shown\n});`,
345
+ },
346
+ });
347
+ }
348
+ return tests;
349
+ }
350
+ /**
351
+ * Extract LMS (Lightning Message Service) behaviors
352
+ */
353
+ function extractLMSBehaviors(transformedMarkup, controller, startId = 1) {
354
+ const tests = [];
355
+ let id = startId;
356
+ for (const lms of transformedMarkup.lmsChannels) {
357
+ if (lms.isPublisherOnly) {
358
+ // Publisher pattern
359
+ tests.push({
360
+ id: `behavior-${id++}`,
361
+ category: 'lms',
362
+ name: `LMS Publisher: ${lms.channelName}`,
363
+ description: `Publishes messages to ${lms.channelName}`,
364
+ auraBehavior: {
365
+ pattern: `<lightning:messageChannel type="${lms.channelName}" aura:id="${lms.auraId}"/>`,
366
+ code: `component.find('${lms.auraId}').publish(payload)`,
367
+ },
368
+ lwcBehavior: {
369
+ pattern: `publish(this.messageContext, CHANNEL, message)`,
370
+ },
371
+ testCode: {
372
+ before: `// Aura: Publishes to ${lms.channelName}`,
373
+ after: generateLmsPublisherAfterTest(lms.channelName),
374
+ },
375
+ });
376
+ }
377
+ else {
378
+ // Subscriber pattern
379
+ tests.push({
380
+ id: `behavior-${id++}`,
381
+ category: 'lms',
382
+ name: `LMS Subscriber: ${lms.channelName}`,
383
+ description: `Subscribes to ${lms.channelName} messages`,
384
+ auraBehavior: {
385
+ pattern: `<lightning:messageChannel type="${lms.channelName}" onMessage="{!c.${lms.onMessageHandler}}"/>`,
386
+ },
387
+ lwcBehavior: {
388
+ pattern: `subscribe(messageContext, CHANNEL, handleMessage) in connectedCallback`,
389
+ },
390
+ testCode: {
391
+ before: `// Aura: Receives messages from ${lms.channelName}`,
392
+ after: generateLmsSubscriberAfterTest(lms.channelName, lms.onMessageHandler || 'handleMessage'),
393
+ },
394
+ });
395
+ // Unsubscribe test
396
+ tests.push({
397
+ id: `behavior-${id++}`,
398
+ category: 'lms',
399
+ name: `LMS Unsubscribe: ${lms.channelName}`,
400
+ description: `Unsubscribes from ${lms.channelName} on destroy`,
401
+ auraBehavior: {
402
+ pattern: 'Automatic cleanup by Aura framework',
403
+ },
404
+ lwcBehavior: {
405
+ pattern: 'unsubscribe(subscription) in disconnectedCallback',
406
+ },
407
+ testCode: {
408
+ before: `// Aura: Framework handles cleanup automatically`,
409
+ after: `test('should unsubscribe on disconnect', () => {\n document.body.appendChild(element);\n document.body.removeChild(element);\n expect(unsubscribe).toHaveBeenCalled();\n});`,
410
+ },
411
+ });
412
+ }
413
+ }
414
+ return tests;
415
+ }
416
+ /**
417
+ * Extract Apex/server call behaviors
418
+ */
419
+ function extractApexBehaviors(markup, controller, helper, startId = 1) {
420
+ const tests = [];
421
+ let id = startId;
422
+ // Find all server calls
423
+ const serverCalls = [];
424
+ if (controller) {
425
+ for (const func of controller.functions) {
426
+ for (const call of func.serverCalls) {
427
+ if (call.controllerMethod) {
428
+ serverCalls.push({ method: call.controllerMethod, calledFrom: func.name });
429
+ }
430
+ }
431
+ }
432
+ }
433
+ if (helper) {
434
+ for (const func of helper.functions) {
435
+ for (const call of func.serverCalls) {
436
+ serverCalls.push({ method: call, calledFrom: `helper.${func.name}` });
437
+ }
438
+ }
439
+ }
440
+ // Create unique server call tests
441
+ const uniqueMethods = [...new Set(serverCalls.map(c => c.method))];
442
+ for (const method of uniqueMethods) {
443
+ const callers = serverCalls.filter(c => c.method === method).map(c => c.calledFrom);
444
+ tests.push({
445
+ id: `behavior-${id++}`,
446
+ category: 'apex',
447
+ name: `Apex call: ${method}`,
448
+ description: `Calls ${markup.controller}.${method} from ${callers.join(', ')}`,
449
+ auraBehavior: {
450
+ pattern: `component.get("c.${method}")`,
451
+ code: `var action = component.get("c.${method}");\naction.setParams({...});\n$A.enqueueAction(action);`,
452
+ },
453
+ lwcBehavior: {
454
+ pattern: `@wire(${method}) or await ${method}({...})`,
455
+ },
456
+ testCode: {
457
+ before: `// Aura: Server action ${method} is called`,
458
+ after: generateApexAfterTest(method, markup.controller || ''),
459
+ },
460
+ });
461
+ }
462
+ return tests;
463
+ }
464
+ // ============ Test Generation Helpers ============
465
+ function generateInitBeforeTest(funcName, setsAttributes, callsServer, callsHelper) {
466
+ let test = `// Aura: When component loads, ${funcName} is invoked\n`;
467
+ test += `// Expected behaviors:\n`;
468
+ if (setsAttributes.length > 0) {
469
+ test += `// - Sets attributes: ${setsAttributes.join(', ')}\n`;
470
+ }
471
+ if (callsServer) {
472
+ test += `// - Makes server call\n`;
473
+ }
474
+ if (callsHelper.length > 0) {
475
+ test += `// - Calls helper: ${callsHelper.join(', ')}\n`;
476
+ }
477
+ return test;
478
+ }
479
+ function generateInitAfterTest(setsAttributes, callsServer) {
480
+ let test = `test('should initialize component state', async () => {\n`;
481
+ test += ` document.body.appendChild(element);\n`;
482
+ test += ` await Promise.resolve(); // Wait for connectedCallback\n\n`;
483
+ if (setsAttributes.length > 0) {
484
+ test += ` // Verify initial state is set\n`;
485
+ for (const attr of setsAttributes) {
486
+ test += ` expect(element.${attr}).toBeDefined();\n`;
487
+ }
488
+ }
489
+ if (callsServer) {
490
+ test += ` // Verify data loading was triggered\n`;
491
+ test += ` // (Check wire adapter or Apex mock was called)\n`;
492
+ }
493
+ test += `});`;
494
+ return test;
495
+ }
496
+ function generatePropertyAfterTest(name, type, defaultVal) {
497
+ let test = `test('should expose ${name} as @api property', () => {\n`;
498
+ test += ` const testValue = ${getTestValueForType(type)};\n`;
499
+ test += ` element.${name} = testValue;\n`;
500
+ test += ` document.body.appendChild(element);\n`;
501
+ test += ` expect(element.${name}).toEqual(testValue);\n`;
502
+ test += `});`;
503
+ if (defaultVal && defaultVal.trim()) {
504
+ test += `\n\ntest('should have default value for ${name}', () => {\n`;
505
+ test += ` document.body.appendChild(element);\n`;
506
+ test += ` expect(element.${name}).toEqual(${defaultVal});\n`;
507
+ test += `});`;
508
+ }
509
+ return test;
510
+ }
511
+ function generateWireAfterTest(recordIdBinding, fields) {
512
+ let test = `test('should wire record data when ${recordIdBinding} is set', async () => {\n`;
513
+ test += ` element.${recordIdBinding} = '001xx000003DGbYAAW';\n`;
514
+ test += ` document.body.appendChild(element);\n`;
515
+ test += ` \n`;
516
+ test += ` // Emit mock wire data\n`;
517
+ test += ` // const mockRecord = { data: { fields: { ${fields.map(f => `${f}: { value: 'test' }`).join(', ')} }}};\n`;
518
+ test += ` // Use @salesforce/sfdx-lwc-jest emit() to send wire data\n`;
519
+ test += ` \n`;
520
+ test += ` await Promise.resolve();\n`;
521
+ test += ` // Verify getters return field values\n`;
522
+ test += `});`;
523
+ return test;
524
+ }
525
+ function generateEventAfterTest(eventName) {
526
+ let test = `test('should dispatch ${eventName} event', () => {\n`;
527
+ test += ` const handler = jest.fn();\n`;
528
+ test += ` element.addEventListener('${eventName.toLowerCase()}', handler);\n`;
529
+ test += ` document.body.appendChild(element);\n`;
530
+ test += ` \n`;
531
+ test += ` // Trigger action that fires event\n`;
532
+ test += ` // e.g., click a button, call a method\n`;
533
+ test += ` \n`;
534
+ test += ` expect(handler).toHaveBeenCalled();\n`;
535
+ test += ` expect(handler.mock.calls[0][0].detail).toBeDefined();\n`;
536
+ test += `});`;
537
+ return test;
538
+ }
539
+ function generateHandlerAfterTest(funcName, eventName, func) {
540
+ let test = `test('should handle ${eventName} event', async () => {\n`;
541
+ test += ` document.body.appendChild(element);\n`;
542
+ test += ` \n`;
543
+ test += ` // Find element that triggers ${funcName}\n`;
544
+ test += ` // const button = element.shadowRoot.querySelector('lightning-button');\n`;
545
+ test += ` // button.click();\n`;
546
+ test += ` \n`;
547
+ test += ` await Promise.resolve();\n`;
548
+ test += ` // Verify expected outcome\n`;
549
+ test += `});`;
550
+ return test;
551
+ }
552
+ function generateLmsPublisherAfterTest(channelName) {
553
+ const channelVar = channelName.replace(/__c$/i, '').toUpperCase() + '_CHANNEL';
554
+ let test = `test('should publish to ${channelName}', () => {\n`;
555
+ test += ` document.body.appendChild(element);\n`;
556
+ test += ` \n`;
557
+ test += ` // Call method that publishes\n`;
558
+ test += ` element.publishMessage({ recordId: '001xx000003DGbY' });\n`;
559
+ test += ` \n`;
560
+ test += ` expect(publish).toHaveBeenCalledWith(\n`;
561
+ test += ` expect.anything(),\n`;
562
+ test += ` ${channelVar},\n`;
563
+ test += ` expect.objectContaining({ recordId: '001xx000003DGbY' })\n`;
564
+ test += ` );\n`;
565
+ test += `});`;
566
+ return test;
567
+ }
568
+ function generateLmsSubscriberAfterTest(channelName, handlerName) {
569
+ const channelVar = channelName.replace(/__c$/i, '').toUpperCase() + '_CHANNEL';
570
+ let test = `test('should subscribe to ${channelName} on connect', () => {\n`;
571
+ test += ` document.body.appendChild(element);\n`;
572
+ test += ` \n`;
573
+ test += ` expect(subscribe).toHaveBeenCalledWith(\n`;
574
+ test += ` expect.anything(),\n`;
575
+ test += ` ${channelVar},\n`;
576
+ test += ` expect.any(Function)\n`;
577
+ test += ` );\n`;
578
+ test += `});\n\n`;
579
+ test += `test('should handle incoming message', async () => {\n`;
580
+ test += ` document.body.appendChild(element);\n`;
581
+ test += ` const messageHandler = subscribe.mock.calls[0][2];\n`;
582
+ test += ` \n`;
583
+ test += ` // Simulate message\n`;
584
+ test += ` messageHandler({ recordId: '001xx000003DGbY' });\n`;
585
+ test += ` await Promise.resolve();\n`;
586
+ test += ` \n`;
587
+ test += ` // Verify component state updated\n`;
588
+ test += `});`;
589
+ return test;
590
+ }
591
+ function generateApexAfterTest(method, controller) {
592
+ let test = `test('should call ${method} Apex method', async () => {\n`;
593
+ test += ` // Mock Apex response\n`;
594
+ test += ` ${method}.mockResolvedValue({ /* expected data */ });\n`;
595
+ test += ` \n`;
596
+ test += ` document.body.appendChild(element);\n`;
597
+ test += ` await Promise.resolve();\n`;
598
+ test += ` \n`;
599
+ test += ` // If using @wire, data loads automatically\n`;
600
+ test += ` // If imperative, call the method that triggers it\n`;
601
+ test += ` \n`;
602
+ test += ` // Verify Apex was called\n`;
603
+ test += ` // expect(${method}).toHaveBeenCalled();\n`;
604
+ test += `});`;
605
+ return test;
606
+ }
607
+ function getTestValueForType(type) {
608
+ switch (type.toLowerCase()) {
609
+ case 'string': return "'test-value'";
610
+ case 'boolean': return 'true';
611
+ case 'integer':
612
+ case 'decimal':
613
+ case 'double':
614
+ case 'number': return '42';
615
+ case 'list':
616
+ case 'object[]': return '[]';
617
+ case 'object':
618
+ case 'map': return '{}';
619
+ default: return "'test-value'";
620
+ }
621
+ }
622
+ // ============ Test File Generators ============
623
+ function generateBeforeTestFile(componentName, tests) {
624
+ let content = `/**
625
+ * BEFORE CONVERSION: Expected Behaviors for ${componentName}
626
+ *
627
+ * This document describes the expected behaviors of the original Aura component.
628
+ * These behaviors should be preserved in the converted LWC.
629
+ *
630
+ * Generated by lwc-convert
631
+ */
632
+
633
+ /**
634
+ * Behavior Inventory
635
+ * ==================
636
+ *
637
+ * The following behaviors were identified in the Aura component:
638
+ */
639
+
640
+ `;
641
+ const categories = [...new Set(tests.map(t => t.category))];
642
+ for (const category of categories) {
643
+ const categoryTests = tests.filter(t => t.category === category);
644
+ content += `// ============ ${category.toUpperCase()} BEHAVIORS ============\n\n`;
645
+ for (const test of categoryTests) {
646
+ content += `/**
647
+ * ${test.id}: ${test.name}
648
+ * ${test.description}
649
+ *
650
+ * Aura Pattern:
651
+ * ${test.auraBehavior.pattern}
652
+ */
653
+ ${test.testCode.before}
654
+
655
+ `;
656
+ }
657
+ }
658
+ content += `
659
+ /**
660
+ * Summary
661
+ * =======
662
+ *
663
+ * Total behaviors identified: ${tests.length}
664
+ * - Lifecycle: ${tests.filter(t => t.category === 'lifecycle').length}
665
+ * - Data: ${tests.filter(t => t.category === 'data').length}
666
+ * - Events: ${tests.filter(t => t.category === 'event').length}
667
+ * - UI: ${tests.filter(t => t.category === 'ui').length}
668
+ * - LMS: ${tests.filter(t => t.category === 'lms').length}
669
+ * - Apex: ${tests.filter(t => t.category === 'apex').length}
670
+ *
671
+ * Run the "after" tests to verify all behaviors are preserved.
672
+ */
673
+ `;
674
+ return content;
675
+ }
676
+ function generateAfterTestFile(lwcName, className, tests, transformedMarkup) {
677
+ const imports = [
678
+ `import { createElement } from 'lwc';`,
679
+ `import ${className} from 'c/${lwcName}';`,
680
+ ];
681
+ const mocks = [];
682
+ // Add LMS mocks if needed
683
+ if (tests.some(t => t.category === 'lms')) {
684
+ imports.push(`import { publish, subscribe, unsubscribe, MessageContext } from 'lightning/messageService';`);
685
+ for (const lms of transformedMarkup.lmsChannels) {
686
+ const channelVar = lms.channelName.replace(/__c$/i, '').toUpperCase() + '_CHANNEL';
687
+ imports.push(`import ${channelVar} from '@salesforce/messageChannel/${lms.channelName}';`);
688
+ }
689
+ mocks.push(`jest.mock(
690
+ 'lightning/messageService',
691
+ () => ({
692
+ publish: jest.fn(),
693
+ subscribe: jest.fn(() => ({ unsubscribe: jest.fn() })),
694
+ unsubscribe: jest.fn(),
695
+ MessageContext: jest.fn()
696
+ }),
697
+ { virtual: true }
698
+ );`);
699
+ }
700
+ // Add wire mocks if needed
701
+ if (tests.some(t => t.category === 'data' && t.lwcBehavior.pattern.includes('@wire'))) {
702
+ imports.push(`import { getRecord, getFieldValue } from 'lightning/uiRecordApi';`);
703
+ }
704
+ // Add Apex mocks if needed
705
+ const apexTests = tests.filter(t => t.category === 'apex');
706
+ for (const test of apexTests) {
707
+ const method = test.name.replace('Apex call: ', '');
708
+ // Extract controller name from description: "Calls ControllerName.method from ..."
709
+ const controllerMatch = test.description.match(/Calls\s+([\w]+)\./);
710
+ const controller = controllerMatch ? controllerMatch[1] : 'ApexController';
711
+ // Add import for the Apex method
712
+ imports.push(`import ${method} from '@salesforce/apex/${controller}.${method}';`);
713
+ mocks.push(`jest.mock(
714
+ '@salesforce/apex/${controller}.${method}',
715
+ () => ({ default: jest.fn() }),
716
+ { virtual: true }
717
+ );`);
718
+ }
719
+ let content = `/**
720
+ * AFTER CONVERSION: Jest Tests for ${lwcName}
721
+ * Converted from Aura component
722
+ *
723
+ * These tests verify that the converted LWC preserves all original behaviors.
724
+ * Each test is linked to a specific behavior from the "before" document.
725
+ *
726
+ * Generated by lwc-convert
727
+ */
728
+
729
+ ${imports.join('\n')}
730
+
731
+ ${mocks.length > 0 ? '\n// Mocks\n' + mocks.join('\n\n') + '\n' : ''}
732
+
733
+ describe('${lwcName} - Behavior Verification', () => {
734
+ let element;
735
+
736
+ beforeEach(() => {
737
+ element = createElement('c-${lwcName}', {
738
+ is: ${className}
739
+ });
740
+ });
741
+
742
+ afterEach(() => {
743
+ while (document.body.firstChild) {
744
+ document.body.removeChild(document.body.firstChild);
745
+ }
746
+ jest.clearAllMocks();
747
+ });
748
+
749
+ `;
750
+ const categories = [...new Set(tests.map(t => t.category))];
751
+ for (const category of categories) {
752
+ const categoryTests = tests.filter(t => t.category === category);
753
+ content += ` // ============ ${category.toUpperCase()} TESTS ============\n`;
754
+ content += ` describe('${category.charAt(0).toUpperCase() + category.slice(1)} Behaviors', () => {\n`;
755
+ for (const test of categoryTests) {
756
+ content += ` /**
757
+ * ${test.id}: ${test.name}
758
+ * LWC Pattern: ${test.lwcBehavior.pattern}
759
+ */
760
+ ${test.testCode.after}
761
+
762
+ `;
763
+ }
764
+ content += ` });\n\n`;
765
+ }
766
+ content += `});
767
+
768
+ /**
769
+ * Test Results Comparison
770
+ * =======================
771
+ *
772
+ * Run these tests with: npm test -- --testPathPattern="${lwcName}"
773
+ *
774
+ * Expected: All ${tests.length} behavior tests should pass
775
+ *
776
+ * If a test fails, compare with the corresponding "before" behavior
777
+ * to understand what the original Aura component did.
778
+ */
779
+ `;
780
+ return content;
781
+ }
782
+ function generateComparisonReport(componentName, lwcName, tests) {
783
+ let report = `# Conversion Behavior Comparison Report
784
+
785
+ ## Component: ${componentName} → ${lwcName}
786
+
787
+ This report documents the behavioral mapping between the original Aura component
788
+ and the converted LWC. Use this to verify the conversion is complete and correct.
789
+
790
+ ## Behavior Summary
791
+
792
+ | Category | Count | Status |
793
+ |----------|-------|--------|
794
+ `;
795
+ const categories = ['lifecycle', 'data', 'event', 'ui', 'lms', 'apex'];
796
+ for (const cat of categories) {
797
+ const count = tests.filter(t => t.category === cat).length;
798
+ const status = count > 0 ? '⏳ Pending verification' : '✓ N/A';
799
+ report += `| ${cat.charAt(0).toUpperCase() + cat.slice(1)} | ${count} | ${status} |\n`;
800
+ }
801
+ report += `| **Total** | **${tests.length}** | |\n`;
802
+ report += `
803
+ ## Detailed Behavior Mapping
804
+
805
+ `;
806
+ for (const cat of categories) {
807
+ const categoryTests = tests.filter(t => t.category === cat);
808
+ if (categoryTests.length === 0)
809
+ continue;
810
+ report += `### ${cat.charAt(0).toUpperCase() + cat.slice(1)} Behaviors
811
+
812
+ `;
813
+ for (const test of categoryTests) {
814
+ report += `#### ${test.id}: ${test.name}
815
+
816
+ - **Description**: ${test.description}
817
+ - **Aura Pattern**: \`${test.auraBehavior.pattern}\`
818
+ - **LWC Pattern**: \`${test.lwcBehavior.pattern}\`
819
+ - **Test Status**: ⬜ Not run
820
+
821
+ `;
822
+ }
823
+ }
824
+ report += `
825
+ ## Verification Checklist
826
+
827
+ Run the test suite and update this checklist:
828
+
829
+ `;
830
+ for (const test of tests) {
831
+ report += `- [ ] ${test.id}: ${test.name}\n`;
832
+ }
833
+ report += `
834
+ ## How to Run Tests
835
+
836
+ \`\`\`bash
837
+ # Run all tests for this component
838
+ npm test -- --testPathPattern="${lwcName}"
839
+
840
+ # Run with coverage
841
+ npm test -- --testPathPattern="${lwcName}" --coverage
842
+
843
+ # Run in watch mode
844
+ npm test -- --testPathPattern="${lwcName}" --watch
845
+ \`\`\`
846
+
847
+ ## Notes
848
+
849
+ - Tests marked with "⏳ Pending verification" need manual review
850
+ - If a test fails, compare the Aura and LWC patterns to understand the difference
851
+ - Some behaviors may require manual implementation (marked as TODO in tests)
852
+ `;
853
+ return report;
854
+ }
855
+ //# sourceMappingURL=test-comparison.js.map