@memberjunction/react-test-harness 2.111.0 → 2.112.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.
- package/dist/lib/component-linter.d.ts +1 -1
- package/dist/lib/component-linter.d.ts.map +1 -1
- package/dist/lib/component-linter.js +1083 -935
- package/dist/lib/component-linter.js.map +1 -1
- package/dist/lib/component-runner.d.ts +1 -1
- package/dist/lib/component-runner.d.ts.map +1 -1
- package/dist/lib/component-runner.js +165 -176
- package/dist/lib/component-runner.js.map +1 -1
- package/dist/lib/library-lint-cache.d.ts +1 -1
- package/dist/lib/library-lint-cache.d.ts.map +1 -1
- package/dist/lib/library-lint-cache.js +5 -7
- package/dist/lib/library-lint-cache.js.map +1 -1
- package/package.json +4 -3
|
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.ComponentRunner = void 0;
|
|
27
|
-
const
|
|
27
|
+
const global_1 = require("@memberjunction/global");
|
|
28
28
|
const component_linter_1 = require("./component-linter");
|
|
29
29
|
const core_entities_1 = require("@memberjunction/core-entities");
|
|
30
30
|
const ai_vectors_memory_1 = require("@memberjunction/ai-vectors-memory");
|
|
@@ -63,10 +63,10 @@ class ComponentRunner {
|
|
|
63
63
|
async lintComponent(componentCode, componentName, componentSpec, isRootComponent, contextUser, options) {
|
|
64
64
|
const lintResult = await component_linter_1.ComponentLinter.lintComponent(componentCode, componentName, componentSpec, isRootComponent, contextUser, false, // debugMode
|
|
65
65
|
options);
|
|
66
|
-
const hasErrors = lintResult.violations.some(v => v.severity === 'critical' || v.severity === 'high');
|
|
66
|
+
const hasErrors = lintResult.violations.some((v) => v.severity === 'critical' || v.severity === 'high');
|
|
67
67
|
return {
|
|
68
68
|
violations: lintResult.violations,
|
|
69
|
-
hasErrors
|
|
69
|
+
hasErrors,
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
async executeComponent(options) {
|
|
@@ -89,7 +89,7 @@ class ComponentRunner {
|
|
|
89
89
|
page.setDefaultTimeout(globalTimeout);
|
|
90
90
|
// Load component metadata and libraries first (needed for library loading)
|
|
91
91
|
await core_entities_1.ComponentMetadataEngine.Instance.Config(false, options.contextUser);
|
|
92
|
-
const allLibraries = core_entities_1.ComponentMetadataEngine.Instance.ComponentLibraries.map(c => c.GetAll());
|
|
92
|
+
const allLibraries = core_entities_1.ComponentMetadataEngine.Instance.ComponentLibraries.map((c) => c.GetAll());
|
|
93
93
|
try {
|
|
94
94
|
// Navigate to a blank page FIRST, then load runtime
|
|
95
95
|
await page.goto('about:blank');
|
|
@@ -101,12 +101,12 @@ class ComponentRunner {
|
|
|
101
101
|
<meta charset="utf-8">
|
|
102
102
|
<title>React Component Test (V2)</title>
|
|
103
103
|
<style>
|
|
104
|
-
body {
|
|
105
|
-
margin: 0;
|
|
106
|
-
padding: 20px;
|
|
104
|
+
body {
|
|
105
|
+
margin: 0;
|
|
106
|
+
padding: 20px;
|
|
107
107
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
108
108
|
}
|
|
109
|
-
#root {
|
|
109
|
+
#root {
|
|
110
110
|
min-height: 100vh;
|
|
111
111
|
background-color: white;
|
|
112
112
|
padding: 20px;
|
|
@@ -130,7 +130,7 @@ class ComponentRunner {
|
|
|
130
130
|
hasReact: typeof window.React !== 'undefined',
|
|
131
131
|
hasReactDOM: typeof window.ReactDOM !== 'undefined',
|
|
132
132
|
hasBabel: typeof window.Babel !== 'undefined',
|
|
133
|
-
mjRuntimeKeys: window.MJReactRuntime ? Object.keys(window.MJReactRuntime) : []
|
|
133
|
+
mjRuntimeKeys: window.MJReactRuntime ? Object.keys(window.MJReactRuntime) : [],
|
|
134
134
|
};
|
|
135
135
|
});
|
|
136
136
|
if (debug) {
|
|
@@ -152,10 +152,10 @@ class ComponentRunner {
|
|
|
152
152
|
console.log(' - props:', JSON.stringify(options.props || {}, null, 2));
|
|
153
153
|
// Show spec-specific libraries, not all available libraries
|
|
154
154
|
if (options.componentSpec.libraries && options.componentSpec.libraries.length > 0) {
|
|
155
|
-
console.log(' - spec requires libraries:', options.componentSpec.libraries.map(lib => ({
|
|
155
|
+
console.log(' - spec requires libraries:', options.componentSpec.libraries.map((lib) => ({
|
|
156
156
|
name: lib.name,
|
|
157
157
|
globalVariable: lib.globalVariable,
|
|
158
|
-
version: lib.version
|
|
158
|
+
version: lib.version,
|
|
159
159
|
})));
|
|
160
160
|
}
|
|
161
161
|
else {
|
|
@@ -213,7 +213,7 @@ class ComponentRunner {
|
|
|
213
213
|
// Diagnostic: Check if React is available before creating context
|
|
214
214
|
if (!window.React) {
|
|
215
215
|
console.error('🔴 CRITICAL: React is NULL when creating runtimeContext!');
|
|
216
|
-
console.error('Window keys:', Object.keys(window).filter(k => k.toLowerCase().includes('react')));
|
|
216
|
+
console.error('Window keys:', Object.keys(window).filter((k) => k.toLowerCase().includes('react')));
|
|
217
217
|
throw new Error('React is not available in window context');
|
|
218
218
|
}
|
|
219
219
|
if (debug) {
|
|
@@ -222,13 +222,13 @@ class ComponentRunner {
|
|
|
222
222
|
useState: typeof window.React?.useState,
|
|
223
223
|
useEffect: typeof window.React?.useEffect,
|
|
224
224
|
useRef: typeof window.React?.useRef,
|
|
225
|
-
useMemo: typeof window.React?.useMemo
|
|
225
|
+
useMemo: typeof window.React?.useMemo,
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
228
|
const runtimeContext = {
|
|
229
229
|
React: window.React,
|
|
230
230
|
ReactDOM: window.ReactDOM,
|
|
231
|
-
libraries: {} // Libraries are loaded internally by the compiler
|
|
231
|
+
libraries: {}, // Libraries are loaded internally by the compiler
|
|
232
232
|
};
|
|
233
233
|
// Create instances with debug mode to see library loading
|
|
234
234
|
const compiler = new ComponentCompiler({ debug: debug });
|
|
@@ -277,7 +277,7 @@ class ComponentRunner {
|
|
|
277
277
|
continue;
|
|
278
278
|
}
|
|
279
279
|
if (!specLib.globalVariable) {
|
|
280
|
-
const libDef = componentLibraries.find(l => l && l.Name && l.Name.toLowerCase() === specLib.name.toLowerCase());
|
|
280
|
+
const libDef = componentLibraries.find((l) => l && l.Name && l.Name.toLowerCase() === specLib.name.toLowerCase());
|
|
281
281
|
if (libDef && libDef.GlobalVariable) {
|
|
282
282
|
specLib.globalVariable = libDef.GlobalVariable;
|
|
283
283
|
if (debug) {
|
|
@@ -289,7 +289,7 @@ class ComponentRunner {
|
|
|
289
289
|
if (debug) {
|
|
290
290
|
console.log('🔍 Spec libraries after enhancement:', spec.libraries.map((l) => ({
|
|
291
291
|
name: l.name,
|
|
292
|
-
globalVariable: l.globalVariable
|
|
292
|
+
globalVariable: l.globalVariable,
|
|
293
293
|
})));
|
|
294
294
|
}
|
|
295
295
|
}
|
|
@@ -315,8 +315,8 @@ class ComponentRunner {
|
|
|
315
315
|
dependencies: spec.dependencies?.map((d) => ({
|
|
316
316
|
name: d.name,
|
|
317
317
|
location: d.location,
|
|
318
|
-
hasCode: !!d.code
|
|
319
|
-
}))
|
|
318
|
+
hasCode: !!d.code,
|
|
319
|
+
})),
|
|
320
320
|
});
|
|
321
321
|
}
|
|
322
322
|
// NEW: Use ComponentManager.loadHierarchy instead of registrar.registerHierarchy
|
|
@@ -328,21 +328,23 @@ class ComponentRunner {
|
|
|
328
328
|
defaultVersion: 'v1',
|
|
329
329
|
returnType: 'both',
|
|
330
330
|
resolutionMode: 'embed', // Convert to embedded format for browser execution
|
|
331
|
-
allLibraries: componentLibraries || [] // Pass libraries for compiler
|
|
331
|
+
allLibraries: componentLibraries || [], // Pass libraries for compiler
|
|
332
332
|
});
|
|
333
333
|
if (debug) {
|
|
334
334
|
console.log('📋 [BROWSER] LoadHierarchy result:', {
|
|
335
335
|
success: loadResult.success,
|
|
336
336
|
rootComponent: !!loadResult.rootComponent,
|
|
337
|
-
resolvedSpec: loadResult.resolvedSpec
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
337
|
+
resolvedSpec: loadResult.resolvedSpec
|
|
338
|
+
? {
|
|
339
|
+
name: loadResult.resolvedSpec.name,
|
|
340
|
+
location: loadResult.resolvedSpec.location,
|
|
341
|
+
registry: loadResult.resolvedSpec.registry,
|
|
342
|
+
libraries: loadResult.resolvedSpec.libraries,
|
|
343
|
+
hasCode: !!loadResult.resolvedSpec.code,
|
|
344
|
+
}
|
|
345
|
+
: null,
|
|
344
346
|
loadedComponents: loadResult.loadedComponents,
|
|
345
|
-
errors: loadResult.errors
|
|
347
|
+
errors: loadResult.errors,
|
|
346
348
|
});
|
|
347
349
|
}
|
|
348
350
|
// Convert to old format for compatibility
|
|
@@ -351,7 +353,7 @@ class ComponentRunner {
|
|
|
351
353
|
registeredComponents: loadResult.loadedComponents,
|
|
352
354
|
errors: loadResult.errors.map((e) => ({ componentName: e.componentName || '', error: e.message, phase: e.phase })),
|
|
353
355
|
warnings: [],
|
|
354
|
-
resolvedSpec: loadResult.resolvedSpec
|
|
356
|
+
resolvedSpec: loadResult.resolvedSpec,
|
|
355
357
|
};
|
|
356
358
|
}
|
|
357
359
|
catch (registrationError) {
|
|
@@ -363,7 +365,7 @@ class ComponentRunner {
|
|
|
363
365
|
stack: registrationError.stack,
|
|
364
366
|
type: 'registration-error',
|
|
365
367
|
phase: 'component-compilation',
|
|
366
|
-
source: 'runtime-wrapper'
|
|
368
|
+
source: 'runtime-wrapper',
|
|
367
369
|
});
|
|
368
370
|
window.__testHarnessTestFailed = true;
|
|
369
371
|
// Don't re-throw - let execution continue to collect this error properly
|
|
@@ -371,7 +373,7 @@ class ComponentRunner {
|
|
|
371
373
|
return {
|
|
372
374
|
success: false,
|
|
373
375
|
error: `Component registration failed: ${registrationError.message || registrationError}`,
|
|
374
|
-
componentCount: 0
|
|
376
|
+
componentCount: 0,
|
|
375
377
|
};
|
|
376
378
|
}
|
|
377
379
|
if (debug && !registrationResult.success) {
|
|
@@ -413,10 +415,7 @@ class ComponentRunner {
|
|
|
413
415
|
antd: typeof window.antd,
|
|
414
416
|
React: typeof window.React,
|
|
415
417
|
ReactDOM: typeof window.ReactDOM,
|
|
416
|
-
windowKeys: Object.keys(window).filter(k => k.toLowerCase().includes('apex') ||
|
|
417
|
-
k.toLowerCase().includes('antd') ||
|
|
418
|
-
k === 'ApexCharts' ||
|
|
419
|
-
k === 'antd')
|
|
418
|
+
windowKeys: Object.keys(window).filter((k) => k.toLowerCase().includes('apex') || k.toLowerCase().includes('antd') || k === 'ApexCharts' || k === 'antd'),
|
|
420
419
|
});
|
|
421
420
|
// If libraries were supposed to be loaded, check their actual presence
|
|
422
421
|
if (spec.libraries && spec.libraries.length > 0) {
|
|
@@ -479,7 +478,7 @@ class ComponentRunner {
|
|
|
479
478
|
'',
|
|
480
479
|
'Inspect all instances where you are using JSX elements that come from libraries or components to ensure they are properly referenced.',
|
|
481
480
|
'',
|
|
482
|
-
'The exact fix depends on the specific library or component structure.'
|
|
481
|
+
'The exact fix depends on the specific library or component structure.',
|
|
483
482
|
].join('\\n');
|
|
484
483
|
// Log to both console and error tracking
|
|
485
484
|
console.error('🔴 Invalid JSX Element Type Detected:', errorMsg);
|
|
@@ -489,7 +488,7 @@ class ComponentRunner {
|
|
|
489
488
|
type: 'invalid-element-type',
|
|
490
489
|
phase: 'createElement',
|
|
491
490
|
source: 'enhanced-detection',
|
|
492
|
-
elementInfo: objectInfo
|
|
491
|
+
elementInfo: objectInfo,
|
|
493
492
|
});
|
|
494
493
|
// Still try to call the original to get React's error too
|
|
495
494
|
// This will provide the component stack trace
|
|
@@ -504,14 +503,14 @@ class ComponentRunner {
|
|
|
504
503
|
'',
|
|
505
504
|
'Inspect how this component is being accessed - it may not exist in the expected location or may have a different name.',
|
|
506
505
|
'',
|
|
507
|
-
'Check that the component exists in your dependencies or libraries and is properly referenced.'
|
|
506
|
+
'Check that the component exists in your dependencies or libraries and is properly referenced.',
|
|
508
507
|
].join('\\n');
|
|
509
508
|
console.error('🔴 Undefined JSX Component:', errorMsg);
|
|
510
509
|
window.__testHarnessRuntimeErrors.push({
|
|
511
510
|
message: errorMsg,
|
|
512
511
|
type: 'undefined-component',
|
|
513
512
|
phase: 'createElement',
|
|
514
|
-
source: 'enhanced-detection'
|
|
513
|
+
source: 'enhanced-detection',
|
|
515
514
|
});
|
|
516
515
|
}
|
|
517
516
|
// Call original createElement
|
|
@@ -540,7 +539,7 @@ class ComponentRunner {
|
|
|
540
539
|
window.__testHarnessRuntimeErrors.push({
|
|
541
540
|
message: `Likely infinite render loop: ${currentRenderCount} createElement calls (max: ${MAX_RENDERS_ALLOWED})`,
|
|
542
541
|
type: 'render-loop',
|
|
543
|
-
source: 'test-harness'
|
|
542
|
+
source: 'test-harness',
|
|
544
543
|
});
|
|
545
544
|
// Try to unmount to stop the madness
|
|
546
545
|
try {
|
|
@@ -574,8 +573,8 @@ class ComponentRunner {
|
|
|
574
573
|
CreateSimpleNotification: (message, style, hideAfter) => {
|
|
575
574
|
console.log('[Test Harness] CreateSimpleNotification called:', { message, style, hideAfter });
|
|
576
575
|
// In test harness, we just log but don't display actual notifications
|
|
577
|
-
}
|
|
578
|
-
}
|
|
576
|
+
},
|
|
577
|
+
},
|
|
579
578
|
};
|
|
580
579
|
if (debug) {
|
|
581
580
|
console.log('🎨 Rendering component with props:', Object.keys(componentProps));
|
|
@@ -604,7 +603,7 @@ class ComponentRunner {
|
|
|
604
603
|
stack: error.stack,
|
|
605
604
|
type: 'react-render-error',
|
|
606
605
|
phase: 'component-render',
|
|
607
|
-
source: 'user-component' // This is the actual error from user's component
|
|
606
|
+
source: 'user-component', // This is the actual error from user's component
|
|
608
607
|
});
|
|
609
608
|
window.__testHarnessTestFailed = true;
|
|
610
609
|
return { hasError: true, error };
|
|
@@ -640,7 +639,7 @@ class ComponentRunner {
|
|
|
640
639
|
}
|
|
641
640
|
return {
|
|
642
641
|
success: true,
|
|
643
|
-
componentCount: registrationResult.registeredComponents.length
|
|
642
|
+
componentCount: registrationResult.registeredComponents.length,
|
|
644
643
|
};
|
|
645
644
|
}
|
|
646
645
|
catch (error) {
|
|
@@ -657,19 +656,19 @@ class ComponentRunner {
|
|
|
657
656
|
message: error.message || String(error),
|
|
658
657
|
stack: error.stack,
|
|
659
658
|
type: 'execution-error',
|
|
660
|
-
source: 'runtime-wrapper'
|
|
659
|
+
source: 'runtime-wrapper',
|
|
661
660
|
});
|
|
662
661
|
window.__testHarnessTestFailed = true;
|
|
663
662
|
return {
|
|
664
663
|
success: false,
|
|
665
|
-
error: error.message || String(error)
|
|
664
|
+
error: error.message || String(error),
|
|
666
665
|
};
|
|
667
666
|
}
|
|
668
667
|
}, {
|
|
669
668
|
spec: options.componentSpec,
|
|
670
669
|
props: options.props,
|
|
671
670
|
debug,
|
|
672
|
-
componentLibraries: allLibraries || []
|
|
671
|
+
componentLibraries: allLibraries || [],
|
|
673
672
|
});
|
|
674
673
|
// Create timeout promise (Recommendation #1)
|
|
675
674
|
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Component execution timeout after ${globalTimeout}ms`)), globalTimeout));
|
|
@@ -685,21 +684,21 @@ class ComponentRunner {
|
|
|
685
684
|
errors.push(`Component execution timed out after ${globalTimeout}ms`);
|
|
686
685
|
executionResult = {
|
|
687
686
|
success: false,
|
|
688
|
-
error: timeoutError instanceof Error ? timeoutError.message : 'Execution timeout'
|
|
687
|
+
error: timeoutError instanceof Error ? timeoutError.message : 'Execution timeout',
|
|
689
688
|
};
|
|
690
689
|
}
|
|
691
690
|
// Ensure executionResult has proper shape
|
|
692
691
|
if (!executionResult) {
|
|
693
692
|
executionResult = {
|
|
694
693
|
success: false,
|
|
695
|
-
error: 'Component execution returned no result'
|
|
694
|
+
error: 'Component execution returned no result',
|
|
696
695
|
};
|
|
697
696
|
errors.push('Component execution failed to return a result');
|
|
698
697
|
}
|
|
699
698
|
else if (!executionResult.success && executionResult.error) {
|
|
700
699
|
// Add the execution error if it hasn't been captured elsewhere
|
|
701
700
|
const errorMsg = `Component execution failed: ${executionResult.error}`;
|
|
702
|
-
if (!errors.some(e => e.includes(executionResult.error))) {
|
|
701
|
+
if (!errors.some((e) => e.includes(executionResult.error))) {
|
|
703
702
|
errors.push(errorMsg);
|
|
704
703
|
}
|
|
705
704
|
}
|
|
@@ -715,13 +714,12 @@ class ComponentRunner {
|
|
|
715
714
|
const runtimeErrorsWithSource = await this.collectRuntimeErrors(page);
|
|
716
715
|
// Filter out JSX element type errors when adding to errors array
|
|
717
716
|
errors.push(...runtimeErrorsWithSource
|
|
718
|
-
.filter(e => {
|
|
717
|
+
.filter((e) => {
|
|
719
718
|
// Skip JSX element type errors from error count
|
|
720
|
-
const isJSXError = e.type === 'invalid-element-type' ||
|
|
721
|
-
e.type === 'undefined-component';
|
|
719
|
+
const isJSXError = e.type === 'invalid-element-type' || e.type === 'undefined-component';
|
|
722
720
|
return !isJSXError;
|
|
723
721
|
})
|
|
724
|
-
.map(e => e.message)); // Extract messages for backward compat
|
|
722
|
+
.map((e) => e.message)); // Extract messages for backward compat
|
|
725
723
|
// Collect warnings (separate from errors)
|
|
726
724
|
const collectedWarnings = await this.collectWarnings(page);
|
|
727
725
|
warnings.push(...collectedWarnings);
|
|
@@ -730,21 +728,20 @@ class ComponentRunner {
|
|
|
730
728
|
await page.waitForTimeout(asyncWaitTime);
|
|
731
729
|
const asyncErrors = await this.collectRuntimeErrors(page);
|
|
732
730
|
// Only add new errors (excluding JSX element type errors)
|
|
733
|
-
asyncErrors.forEach(err => {
|
|
734
|
-
const isJSXError = err.type === 'invalid-element-type' ||
|
|
735
|
-
err.type === 'undefined-component';
|
|
731
|
+
asyncErrors.forEach((err) => {
|
|
732
|
+
const isJSXError = err.type === 'invalid-element-type' || err.type === 'undefined-component';
|
|
736
733
|
if (!isJSXError && !errors.includes(err.message)) {
|
|
737
734
|
errors.push(err.message);
|
|
738
735
|
runtimeErrorsWithSource.push(err); // Keep the structured version too
|
|
739
736
|
}
|
|
740
|
-
else if (isJSXError && !runtimeErrorsWithSource.some(e => e.message === err.message)) {
|
|
737
|
+
else if (isJSXError && !runtimeErrorsWithSource.some((e) => e.message === err.message)) {
|
|
741
738
|
// Still track JSX errors for logging but don't add to errors array
|
|
742
739
|
runtimeErrorsWithSource.push(err);
|
|
743
740
|
}
|
|
744
741
|
});
|
|
745
742
|
// Also check for new warnings
|
|
746
743
|
const asyncWarnings = await this.collectWarnings(page);
|
|
747
|
-
asyncWarnings.forEach(warn => {
|
|
744
|
+
asyncWarnings.forEach((warn) => {
|
|
748
745
|
if (!warnings.includes(warn)) {
|
|
749
746
|
warnings.push(warn);
|
|
750
747
|
}
|
|
@@ -759,16 +756,13 @@ class ComponentRunner {
|
|
|
759
756
|
errors.push(`Possible render loop: ${renderCount} createElement calls detected (likely infinite loop)`);
|
|
760
757
|
}
|
|
761
758
|
// Determine success
|
|
762
|
-
const success = errors.length === 0 &&
|
|
763
|
-
!hasRenderLoop &&
|
|
764
|
-
!hasTimeout &&
|
|
765
|
-
executionResult.success;
|
|
759
|
+
const success = errors.length === 0 && !hasRenderLoop && !hasTimeout && executionResult.success;
|
|
766
760
|
// Combine runtime errors with data errors
|
|
767
761
|
const allErrors = [...errors, ...dataErrors];
|
|
768
762
|
// Map runtime errors with source info and specific rules
|
|
769
763
|
// Filter out JSX element type errors - they're too noisy and often false positives
|
|
770
764
|
const errorViolations = runtimeErrorsWithSource
|
|
771
|
-
.filter(e => {
|
|
765
|
+
.filter((e) => {
|
|
772
766
|
// Skip JSX element type errors - still logged but not reported as violations
|
|
773
767
|
const isJSXError = e.rule === 'invalid-jsx-element' ||
|
|
774
768
|
e.rule === 'undefined-jsx-component' ||
|
|
@@ -779,13 +773,13 @@ class ComponentRunner {
|
|
|
779
773
|
}
|
|
780
774
|
return !isJSXError;
|
|
781
775
|
})
|
|
782
|
-
.map(e => ({
|
|
776
|
+
.map((e) => ({
|
|
783
777
|
message: e.message,
|
|
784
778
|
severity: 'critical',
|
|
785
779
|
rule: e.rule || 'runtime-error', // Use specific rule from collectRuntimeErrors
|
|
786
780
|
line: 0,
|
|
787
781
|
column: 0,
|
|
788
|
-
source: e.source
|
|
782
|
+
source: e.source,
|
|
789
783
|
}));
|
|
790
784
|
// Add timeout error if detected
|
|
791
785
|
if (hasTimeout) {
|
|
@@ -795,7 +789,7 @@ class ComponentRunner {
|
|
|
795
789
|
rule: 'timeout',
|
|
796
790
|
line: 0,
|
|
797
791
|
column: 0,
|
|
798
|
-
source: 'test-harness' // This is a test harness timeout
|
|
792
|
+
source: 'test-harness', // This is a test harness timeout
|
|
799
793
|
});
|
|
800
794
|
}
|
|
801
795
|
// Add render loop error if detected
|
|
@@ -806,25 +800,25 @@ class ComponentRunner {
|
|
|
806
800
|
rule: 'render-loop',
|
|
807
801
|
line: 0,
|
|
808
802
|
column: 0,
|
|
809
|
-
source: 'test-harness' // This is a test harness detection
|
|
803
|
+
source: 'test-harness', // This is a test harness detection
|
|
810
804
|
});
|
|
811
805
|
}
|
|
812
806
|
// Add data errors without source
|
|
813
|
-
dataErrors.forEach(e => {
|
|
807
|
+
dataErrors.forEach((e) => {
|
|
814
808
|
errorViolations.push({
|
|
815
809
|
message: e,
|
|
816
810
|
severity: 'critical',
|
|
817
811
|
rule: 'runtime-error',
|
|
818
812
|
line: 0,
|
|
819
813
|
column: 0,
|
|
820
|
-
source: 'user-component' // Data errors are from user's data access code
|
|
814
|
+
source: 'user-component', // Data errors are from user's data access code
|
|
821
815
|
});
|
|
822
816
|
});
|
|
823
817
|
// Check warnings for critical patterns and move them to errors
|
|
824
818
|
const criticalWarningViolations = [];
|
|
825
819
|
const regularWarnings = [];
|
|
826
|
-
warnings.forEach(w => {
|
|
827
|
-
if (ComponentRunner.CRITICAL_WARNING_PATTERNS.some(pattern => pattern.test(w))) {
|
|
820
|
+
warnings.forEach((w) => {
|
|
821
|
+
if (ComponentRunner.CRITICAL_WARNING_PATTERNS.some((pattern) => pattern.test(w))) {
|
|
828
822
|
// This is a critical warning - add to errors
|
|
829
823
|
criticalWarningViolations.push({
|
|
830
824
|
message: w,
|
|
@@ -832,7 +826,7 @@ class ComponentRunner {
|
|
|
832
826
|
rule: 'critical-react-warning',
|
|
833
827
|
line: 0,
|
|
834
828
|
column: 0,
|
|
835
|
-
source: 'react-framework'
|
|
829
|
+
source: 'react-framework',
|
|
836
830
|
});
|
|
837
831
|
}
|
|
838
832
|
else {
|
|
@@ -846,17 +840,17 @@ class ComponentRunner {
|
|
|
846
840
|
success: success && dataErrors.length === 0 && criticalWarningViolations.length === 0, // Fail on critical warnings too
|
|
847
841
|
html,
|
|
848
842
|
errors: allErrorViolations,
|
|
849
|
-
warnings: regularWarnings.map(w => ({
|
|
843
|
+
warnings: regularWarnings.map((w) => ({
|
|
850
844
|
message: w,
|
|
851
845
|
severity: 'low',
|
|
852
846
|
rule: 'warning',
|
|
853
847
|
line: 0,
|
|
854
|
-
column: 0
|
|
848
|
+
column: 0,
|
|
855
849
|
})),
|
|
856
850
|
console: consoleLogs,
|
|
857
851
|
screenshot,
|
|
858
852
|
executionTime: Date.now() - startTime,
|
|
859
|
-
renderCount
|
|
853
|
+
renderCount,
|
|
860
854
|
};
|
|
861
855
|
if (debug) {
|
|
862
856
|
this.dumpDebugInfo(result);
|
|
@@ -867,42 +861,44 @@ class ComponentRunner {
|
|
|
867
861
|
// For catch block errors, we need to handle them specially
|
|
868
862
|
const catchError = {
|
|
869
863
|
message: error instanceof Error ? error.message : String(error),
|
|
870
|
-
source: 'test-harness' // Errors caught here are usually test harness issues
|
|
864
|
+
source: 'test-harness', // Errors caught here are usually test harness issues
|
|
871
865
|
};
|
|
872
866
|
// Create error violations including the catch error
|
|
873
|
-
const errorViolations = [
|
|
867
|
+
const errorViolations = [
|
|
868
|
+
{
|
|
874
869
|
message: catchError.message,
|
|
875
870
|
severity: 'critical',
|
|
876
871
|
rule: 'runtime-error',
|
|
877
872
|
line: 0,
|
|
878
873
|
column: 0,
|
|
879
|
-
source: catchError.source
|
|
880
|
-
}
|
|
874
|
+
source: catchError.source,
|
|
875
|
+
},
|
|
876
|
+
];
|
|
881
877
|
// Add any data errors
|
|
882
|
-
dataErrors.forEach(e => {
|
|
878
|
+
dataErrors.forEach((e) => {
|
|
883
879
|
errorViolations.push({
|
|
884
880
|
message: e,
|
|
885
881
|
severity: 'critical',
|
|
886
882
|
rule: 'runtime-error',
|
|
887
883
|
line: 0,
|
|
888
884
|
column: 0,
|
|
889
|
-
source: 'user-component'
|
|
885
|
+
source: 'user-component',
|
|
890
886
|
});
|
|
891
887
|
});
|
|
892
888
|
const result = {
|
|
893
889
|
success: false,
|
|
894
890
|
html: '',
|
|
895
891
|
errors: errorViolations,
|
|
896
|
-
warnings: warnings.map(w => ({
|
|
892
|
+
warnings: warnings.map((w) => ({
|
|
897
893
|
message: w,
|
|
898
894
|
severity: 'low',
|
|
899
895
|
rule: 'warning',
|
|
900
896
|
line: 0,
|
|
901
|
-
column: 0
|
|
897
|
+
column: 0,
|
|
902
898
|
})),
|
|
903
899
|
console: consoleLogs,
|
|
904
900
|
executionTime: Date.now() - startTime,
|
|
905
|
-
renderCount
|
|
901
|
+
renderCount,
|
|
906
902
|
};
|
|
907
903
|
if (debug) {
|
|
908
904
|
console.log('\n❌ Component execution failed with error:', error);
|
|
@@ -940,7 +936,7 @@ class ComponentRunner {
|
|
|
940
936
|
try {
|
|
941
937
|
await Promise.race([
|
|
942
938
|
page.addScriptTag({ url }),
|
|
943
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error(`Script load timeout: ${url}`)), timeout))
|
|
939
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Script load timeout: ${url}`)), timeout)),
|
|
944
940
|
]);
|
|
945
941
|
}
|
|
946
942
|
catch (error) {
|
|
@@ -978,7 +974,7 @@ class ComponentRunner {
|
|
|
978
974
|
React: typeof window.React !== 'undefined',
|
|
979
975
|
ReactDOM: typeof window.ReactDOM !== 'undefined',
|
|
980
976
|
Babel: typeof window.Babel !== 'undefined',
|
|
981
|
-
MJRuntime: typeof window.MJReactRuntime !== 'undefined'
|
|
977
|
+
MJRuntime: typeof window.MJReactRuntime !== 'undefined',
|
|
982
978
|
};
|
|
983
979
|
});
|
|
984
980
|
// All core libraries loaded successfully
|
|
@@ -997,10 +993,10 @@ class ComponentRunner {
|
|
|
997
993
|
async loadComponentLibraries(page, specLibraries, allLibraries, debug = false) {
|
|
998
994
|
if (debug) {
|
|
999
995
|
console.log('📚 Loading component libraries:');
|
|
1000
|
-
console.log(' 🔍 Component requires libraries:', specLibraries.map(l => l.name));
|
|
996
|
+
console.log(' 🔍 Component requires libraries:', specLibraries.map((l) => l.name));
|
|
1001
997
|
console.log(' 📦 Total available ComponentLibrary entries:', allLibraries.length);
|
|
1002
998
|
console.log(' 📋 Sample of available libraries (first 10):');
|
|
1003
|
-
allLibraries.slice(0, 10).forEach(lib => {
|
|
999
|
+
allLibraries.slice(0, 10).forEach((lib) => {
|
|
1004
1000
|
console.log(` - ${lib.Name}@${lib.Version} (${lib.Status})`);
|
|
1005
1001
|
});
|
|
1006
1002
|
}
|
|
@@ -1020,12 +1016,12 @@ class ComponentRunner {
|
|
|
1020
1016
|
console.log(`📦 Loading ${specLib.name}:`, {
|
|
1021
1017
|
cdnUrl: libDef.CDNUrl,
|
|
1022
1018
|
globalVariable: libDef.GlobalVariable,
|
|
1023
|
-
dependencies: libDef.Dependencies ? JSON.parse(libDef.Dependencies) : null
|
|
1019
|
+
dependencies: libDef.Dependencies ? JSON.parse(libDef.Dependencies) : null,
|
|
1024
1020
|
});
|
|
1025
1021
|
}
|
|
1026
1022
|
// Load CSS if available
|
|
1027
1023
|
if (libDef.CDNCssUrl) {
|
|
1028
|
-
const cssUrls = libDef.CDNCssUrl.split(',').map(url => url.trim());
|
|
1024
|
+
const cssUrls = libDef.CDNCssUrl.split(',').map((url) => url.trim());
|
|
1029
1025
|
for (const cssUrl of cssUrls) {
|
|
1030
1026
|
if (cssUrl) {
|
|
1031
1027
|
await page.addStyleTag({ url: cssUrl });
|
|
@@ -1041,7 +1037,7 @@ class ComponentRunner {
|
|
|
1041
1037
|
// Add timeout for library loading (Recommendation #3)
|
|
1042
1038
|
await Promise.race([
|
|
1043
1039
|
page.addScriptTag({ url: libDef.CDNUrl }),
|
|
1044
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error(`Library load timeout: ${libDef.CDNUrl}`)), 10000))
|
|
1040
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Library load timeout: ${libDef.CDNUrl}`)), 10000)),
|
|
1045
1041
|
]);
|
|
1046
1042
|
// Verify the library loaded
|
|
1047
1043
|
const isLoaded = await page.evaluate((globalVar) => {
|
|
@@ -1068,10 +1064,12 @@ class ComponentRunner {
|
|
|
1068
1064
|
if (debug) {
|
|
1069
1065
|
// Log all available global variables that look like libraries
|
|
1070
1066
|
// Get all the global variables we expect from the spec
|
|
1071
|
-
const expectedGlobals = specLibraries
|
|
1067
|
+
const expectedGlobals = specLibraries
|
|
1068
|
+
.map((lib) => {
|
|
1072
1069
|
const libDef = libraryMap.get(lib.name.toLowerCase());
|
|
1073
1070
|
return libDef?.GlobalVariable;
|
|
1074
|
-
})
|
|
1071
|
+
})
|
|
1072
|
+
.filter(Boolean);
|
|
1075
1073
|
const globals = await page.evaluate((expected) => {
|
|
1076
1074
|
const relevantGlobals = {};
|
|
1077
1075
|
// Check the expected globals from the spec
|
|
@@ -1145,7 +1143,7 @@ class ComponentRunner {
|
|
|
1145
1143
|
'',
|
|
1146
1144
|
'Inspect all instances where you are using JSX elements that come from libraries or components to ensure they are properly referenced.',
|
|
1147
1145
|
'',
|
|
1148
|
-
'The exact fix depends on the specific library or component structure.'
|
|
1146
|
+
'The exact fix depends on the specific library or component structure.',
|
|
1149
1147
|
].join('\\n');
|
|
1150
1148
|
// Log to both console and error tracking
|
|
1151
1149
|
console.error('🔴 Invalid JSX Element Type Detected:', errorMsg);
|
|
@@ -1156,7 +1154,7 @@ class ComponentRunner {
|
|
|
1156
1154
|
type: 'invalid-element-type',
|
|
1157
1155
|
phase: 'createElement',
|
|
1158
1156
|
source: 'enhanced-detection',
|
|
1159
|
-
elementInfo: objectInfo
|
|
1157
|
+
elementInfo: objectInfo,
|
|
1160
1158
|
});
|
|
1161
1159
|
// Still try to call the original to get React's error too
|
|
1162
1160
|
// This will provide the component stack trace
|
|
@@ -1171,7 +1169,7 @@ class ComponentRunner {
|
|
|
1171
1169
|
'',
|
|
1172
1170
|
'Inspect how this component is being accessed - it may not exist in the expected location or may have a different name.',
|
|
1173
1171
|
'',
|
|
1174
|
-
'Check that the component exists in your dependencies or libraries and is properly referenced.'
|
|
1172
|
+
'Check that the component exists in your dependencies or libraries and is properly referenced.',
|
|
1175
1173
|
].join('\\n');
|
|
1176
1174
|
console.error('🔴 Undefined JSX Component:', errorMsg);
|
|
1177
1175
|
window.__testHarnessRuntimeErrors = window.__testHarnessRuntimeErrors || [];
|
|
@@ -1179,7 +1177,7 @@ class ComponentRunner {
|
|
|
1179
1177
|
message: errorMsg,
|
|
1180
1178
|
type: 'undefined-component',
|
|
1181
1179
|
phase: 'createElement',
|
|
1182
|
-
source: 'enhanced-detection'
|
|
1180
|
+
source: 'enhanced-detection',
|
|
1183
1181
|
});
|
|
1184
1182
|
}
|
|
1185
1183
|
// Call original createElement
|
|
@@ -1189,7 +1187,7 @@ class ComponentRunner {
|
|
|
1189
1187
|
// Override console.error
|
|
1190
1188
|
const originalConsoleError = console.error;
|
|
1191
1189
|
console.error = function (...args) {
|
|
1192
|
-
const errorText = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ');
|
|
1190
|
+
const errorText = args.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))).join(' ');
|
|
1193
1191
|
// Check if this is a warning rather than an error
|
|
1194
1192
|
// React warnings typically start with "Warning:" or contain warning-related text
|
|
1195
1193
|
const isWarning = errorText.includes('Warning:') ||
|
|
@@ -1220,16 +1218,15 @@ class ComponentRunner {
|
|
|
1220
1218
|
isAvailableLibrary: false,
|
|
1221
1219
|
matchedLibrary: null,
|
|
1222
1220
|
specLibraries: spec?.libraries || [],
|
|
1223
|
-
specDependencies: spec?.dependencies || []
|
|
1221
|
+
specDependencies: spec?.dependencies || [],
|
|
1224
1222
|
};
|
|
1225
1223
|
// Check if it's in spec libraries
|
|
1226
1224
|
result.isInSpecLibraries = result.specLibraries.some((lib) => lib.globalVariable === identifier);
|
|
1227
|
-
// Check if it's in spec dependencies
|
|
1225
|
+
// Check if it's in spec dependencies
|
|
1228
1226
|
result.isInSpecDependencies = result.specDependencies.some((dep) => dep.name === identifier);
|
|
1229
1227
|
// Check against ALL available libraries (case-insensitive)
|
|
1230
1228
|
if (availableLibraries) {
|
|
1231
|
-
const availableLib = availableLibraries.find((lib) => lib.GlobalVariable &&
|
|
1232
|
-
lib.GlobalVariable.toLowerCase() === identifier.toLowerCase());
|
|
1229
|
+
const availableLib = availableLibraries.find((lib) => lib.GlobalVariable && lib.GlobalVariable.toLowerCase() === identifier.toLowerCase());
|
|
1233
1230
|
if (availableLib) {
|
|
1234
1231
|
result.isAvailableLibrary = true;
|
|
1235
1232
|
result.matchedLibrary = availableLib;
|
|
@@ -1242,29 +1239,35 @@ class ComponentRunner {
|
|
|
1242
1239
|
// Case 1: Trying to use a library not in their spec
|
|
1243
1240
|
if (analysis.isAvailableLibrary && !analysis.isInSpecLibraries) {
|
|
1244
1241
|
const libList = analysis.specLibraries.length > 0
|
|
1245
|
-
? analysis.specLibraries
|
|
1242
|
+
? analysis.specLibraries
|
|
1243
|
+
.map((l) => l.globalVariable || l.name)
|
|
1244
|
+
.filter(Boolean)
|
|
1245
|
+
.join(', ')
|
|
1246
1246
|
: 'no third-party libraries';
|
|
1247
|
-
return `${identifier} is not defined. It appears you're trying to use the ${analysis.matchedLibrary.Name} library. ` +
|
|
1247
|
+
return (`${identifier} is not defined. It appears you're trying to use the ${analysis.matchedLibrary.Name} library. ` +
|
|
1248
1248
|
`You do NOT have access to this library. ` +
|
|
1249
1249
|
`Your architect gave you access to: ${libList}. ` +
|
|
1250
|
-
`You must work within these constraints and cannot load additional libraries
|
|
1250
|
+
`You must work within these constraints and cannot load additional libraries.`);
|
|
1251
1251
|
}
|
|
1252
1252
|
// Case 2: Should be a component but not properly accessed
|
|
1253
1253
|
if (analysis.isInSpecDependencies) {
|
|
1254
|
-
return `${identifier} is not defined. This component exists in your dependencies. ` +
|
|
1254
|
+
return (`${identifier} is not defined. This component exists in your dependencies. ` +
|
|
1255
1255
|
`Ensure you've destructured it: const { ${identifier} } = components; ` +
|
|
1256
|
-
`or accessed it as: components.${identifier}
|
|
1256
|
+
`or accessed it as: components.${identifier}`);
|
|
1257
1257
|
}
|
|
1258
1258
|
// Case 3: Not a valid library or component
|
|
1259
1259
|
const libList = analysis.specLibraries.length > 0
|
|
1260
|
-
? `Available libraries: ${analysis.specLibraries
|
|
1260
|
+
? `Available libraries: ${analysis.specLibraries
|
|
1261
|
+
.map((l) => l.globalVariable || l.name)
|
|
1262
|
+
.filter(Boolean)
|
|
1263
|
+
.join(', ')}`
|
|
1261
1264
|
: 'No third-party libraries are available';
|
|
1262
1265
|
const depList = analysis.specDependencies.length > 0
|
|
1263
1266
|
? `Available components: ${analysis.specDependencies.map((d) => d.name).join(', ')}`
|
|
1264
1267
|
: 'No component dependencies are available';
|
|
1265
|
-
return `${identifier} is not defined. This is not a valid library or component in your specification. ` +
|
|
1268
|
+
return (`${identifier} is not defined. This is not a valid library or component in your specification. ` +
|
|
1266
1269
|
`${libList}. ${depList}. ` +
|
|
1267
|
-
`You must only use the libraries and components specified in your component specification
|
|
1270
|
+
`You must only use the libraries and components specified in your component specification.`);
|
|
1268
1271
|
};
|
|
1269
1272
|
// Global error handler
|
|
1270
1273
|
window.addEventListener('error', (event) => {
|
|
@@ -1282,7 +1285,7 @@ class ComponentRunner {
|
|
|
1282
1285
|
stack: event.error?.stack,
|
|
1283
1286
|
type: 'undefined-identifier',
|
|
1284
1287
|
source: 'user-component',
|
|
1285
|
-
identifier: identifier
|
|
1288
|
+
identifier: identifier,
|
|
1286
1289
|
});
|
|
1287
1290
|
window.__testHarnessTestFailed = true;
|
|
1288
1291
|
return;
|
|
@@ -1296,7 +1299,7 @@ class ComponentRunner {
|
|
|
1296
1299
|
message: event.error?.message || event.message,
|
|
1297
1300
|
stack: event.error?.stack,
|
|
1298
1301
|
type: 'runtime',
|
|
1299
|
-
source: source
|
|
1302
|
+
source: source,
|
|
1300
1303
|
});
|
|
1301
1304
|
window.__testHarnessTestFailed = true;
|
|
1302
1305
|
});
|
|
@@ -1306,7 +1309,7 @@ class ComponentRunner {
|
|
|
1306
1309
|
message: 'Unhandled Promise Rejection: ' + (event.reason?.message || event.reason),
|
|
1307
1310
|
stack: event.reason?.stack,
|
|
1308
1311
|
type: 'promise-rejection',
|
|
1309
|
-
source: 'user-component' // Async errors are likely from user code
|
|
1312
|
+
source: 'user-component', // Async errors are likely from user code
|
|
1310
1313
|
});
|
|
1311
1314
|
window.__testHarnessTestFailed = true;
|
|
1312
1315
|
event.preventDefault();
|
|
@@ -1321,20 +1324,17 @@ class ComponentRunner {
|
|
|
1321
1324
|
return {
|
|
1322
1325
|
runtimeErrors: window.__testHarnessRuntimeErrors || [],
|
|
1323
1326
|
consoleErrors: window.__testHarnessConsoleErrors || [],
|
|
1324
|
-
testFailed: window.__testHarnessTestFailed || false
|
|
1327
|
+
testFailed: window.__testHarnessTestFailed || false,
|
|
1325
1328
|
};
|
|
1326
1329
|
});
|
|
1327
1330
|
// Track unique errors and their counts
|
|
1328
1331
|
const errorMap = new Map();
|
|
1329
1332
|
// Check if we have any specific React render errors
|
|
1330
|
-
const hasSpecificReactError = errorData.runtimeErrors.some((error) => error.type === 'react-render-error' &&
|
|
1331
|
-
!error.message.includes('Script error'));
|
|
1333
|
+
const hasSpecificReactError = errorData.runtimeErrors.some((error) => error.type === 'react-render-error' && !error.message.includes('Script error'));
|
|
1332
1334
|
// Process runtime errors with their source information
|
|
1333
1335
|
errorData.runtimeErrors.forEach((error) => {
|
|
1334
1336
|
// Skip generic "Script error" messages if we have more specific React errors
|
|
1335
|
-
if (hasSpecificReactError &&
|
|
1336
|
-
error.type === 'runtime' &&
|
|
1337
|
-
error.message === 'Script error.') {
|
|
1337
|
+
if (hasSpecificReactError && error.type === 'runtime' && error.message === 'Script error.') {
|
|
1338
1338
|
return; // Skip this generic error
|
|
1339
1339
|
}
|
|
1340
1340
|
// Map error types to specific rule names
|
|
@@ -1381,9 +1381,9 @@ class ComponentRunner {
|
|
|
1381
1381
|
message: error.message,
|
|
1382
1382
|
source: error.source,
|
|
1383
1383
|
type: error.type,
|
|
1384
|
-
rule: rule
|
|
1384
|
+
rule: rule,
|
|
1385
1385
|
},
|
|
1386
|
-
count: 1
|
|
1386
|
+
count: 1,
|
|
1387
1387
|
});
|
|
1388
1388
|
}
|
|
1389
1389
|
});
|
|
@@ -1399,9 +1399,9 @@ class ComponentRunner {
|
|
|
1399
1399
|
message: error,
|
|
1400
1400
|
source: 'react-framework',
|
|
1401
1401
|
type: 'console-error',
|
|
1402
|
-
rule: 'console-error'
|
|
1402
|
+
rule: 'console-error',
|
|
1403
1403
|
},
|
|
1404
|
-
count: 1
|
|
1404
|
+
count: 1,
|
|
1405
1405
|
});
|
|
1406
1406
|
}
|
|
1407
1407
|
});
|
|
@@ -1409,12 +1409,10 @@ class ComponentRunner {
|
|
|
1409
1409
|
const errors = [];
|
|
1410
1410
|
errorMap.forEach(({ error, count }) => {
|
|
1411
1411
|
// Append count if > 1
|
|
1412
|
-
const message = count > 1
|
|
1413
|
-
? `${error.message} (occurred ${count} times)`
|
|
1414
|
-
: error.message;
|
|
1412
|
+
const message = count > 1 ? `${error.message} (occurred ${count} times)` : error.message;
|
|
1415
1413
|
errors.push({
|
|
1416
1414
|
...error,
|
|
1417
|
-
message
|
|
1415
|
+
message,
|
|
1418
1416
|
});
|
|
1419
1417
|
});
|
|
1420
1418
|
return errors;
|
|
@@ -1425,7 +1423,7 @@ class ComponentRunner {
|
|
|
1425
1423
|
async collectWarnings(page) {
|
|
1426
1424
|
const warningData = await page.evaluate(() => {
|
|
1427
1425
|
return {
|
|
1428
|
-
consoleWarnings: window.__testHarnessConsoleWarnings || []
|
|
1426
|
+
consoleWarnings: window.__testHarnessConsoleWarnings || [],
|
|
1429
1427
|
};
|
|
1430
1428
|
});
|
|
1431
1429
|
const warnings = [];
|
|
@@ -1457,23 +1455,23 @@ class ComponentRunner {
|
|
|
1457
1455
|
});
|
|
1458
1456
|
}
|
|
1459
1457
|
async buildLocalMJUtilities(contextUser) {
|
|
1460
|
-
console.log(
|
|
1461
|
-
const rv = new
|
|
1462
|
-
const rq = new
|
|
1463
|
-
const md = new
|
|
1458
|
+
console.log(' Building local MJ utilities');
|
|
1459
|
+
const rv = new global_1.RunView();
|
|
1460
|
+
const rq = new global_1.RunQuery();
|
|
1461
|
+
const md = new global_1.Metadata();
|
|
1464
1462
|
return {
|
|
1465
1463
|
rv: {
|
|
1466
1464
|
RunView: rv.RunView,
|
|
1467
|
-
RunViews: rv.RunViews
|
|
1465
|
+
RunViews: rv.RunViews,
|
|
1468
1466
|
},
|
|
1469
1467
|
rq: {
|
|
1470
|
-
RunQuery: rq.RunQuery
|
|
1468
|
+
RunQuery: rq.RunQuery,
|
|
1471
1469
|
},
|
|
1472
1470
|
md: {
|
|
1473
1471
|
GetEntityObject: md.GetEntityObject, // return the function
|
|
1474
|
-
Entities: md.Entities // return the function
|
|
1472
|
+
Entities: md.Entities, // return the function
|
|
1475
1473
|
},
|
|
1476
|
-
ai: await this.BuildLocalSimpleAITools(contextUser)
|
|
1474
|
+
ai: await this.BuildLocalSimpleAITools(contextUser),
|
|
1477
1475
|
};
|
|
1478
1476
|
}
|
|
1479
1477
|
async BuildLocalSimpleAITools(contextUser) {
|
|
@@ -1490,8 +1488,7 @@ class ComponentRunner {
|
|
|
1490
1488
|
await aiEngine.Config(false, params.contextUser);
|
|
1491
1489
|
const models = aiEngine.Models;
|
|
1492
1490
|
for (const preferredModel of params.preferredModels) {
|
|
1493
|
-
model = models.find((m) => m.Name === preferredModel &&
|
|
1494
|
-
m.IsActive === true);
|
|
1491
|
+
model = models.find((m) => m.Name === preferredModel && m.IsActive === true);
|
|
1495
1492
|
if (model)
|
|
1496
1493
|
break;
|
|
1497
1494
|
}
|
|
@@ -1501,8 +1498,7 @@ class ComponentRunner {
|
|
|
1501
1498
|
if (params.modelPower === 'lowest') {
|
|
1502
1499
|
// Get lowest power model by sorting in reverse
|
|
1503
1500
|
await aiEngine.Config(false, params.contextUser);
|
|
1504
|
-
const llmModels = aiEngine.Models.filter((m) => m.AIModelType === 'LLM' &&
|
|
1505
|
-
m.IsActive === true);
|
|
1501
|
+
const llmModels = aiEngine.Models.filter((m) => m.AIModelType === 'LLM' && m.IsActive === true);
|
|
1506
1502
|
model = llmModels.sort((a, b) => (a.PowerRank || 0) - (b.PowerRank || 0))[0];
|
|
1507
1503
|
}
|
|
1508
1504
|
else if (params.modelPower === 'highest') {
|
|
@@ -1511,8 +1507,7 @@ class ComponentRunner {
|
|
|
1511
1507
|
else {
|
|
1512
1508
|
// Default to medium - get a model in the middle range
|
|
1513
1509
|
await aiEngine.Config(false, params.contextUser);
|
|
1514
|
-
const llmModels = aiEngine.Models.filter((m) => m.AIModelType === 'LLM' &&
|
|
1515
|
-
m.IsActive === true);
|
|
1510
|
+
const llmModels = aiEngine.Models.filter((m) => m.AIModelType === 'LLM' && m.IsActive === true);
|
|
1516
1511
|
const sortedModels = llmModels.sort((a, b) => (b.PowerRank || 0) - (a.PowerRank || 0));
|
|
1517
1512
|
const midIndex = Math.floor(sortedModels.length / 2);
|
|
1518
1513
|
model = sortedModels[midIndex] || sortedModels[0];
|
|
@@ -1521,9 +1516,7 @@ class ComponentRunner {
|
|
|
1521
1516
|
// Build full conversation from messages if provided
|
|
1522
1517
|
let fullUserPrompt = '';
|
|
1523
1518
|
if (params.messages && params.messages.length > 0) {
|
|
1524
|
-
fullUserPrompt = params.messages
|
|
1525
|
-
.map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.message}`)
|
|
1526
|
-
.join('\n');
|
|
1519
|
+
fullUserPrompt = params.messages.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.message}`).join('\n');
|
|
1527
1520
|
}
|
|
1528
1521
|
// Execute the prompt using AIEngine
|
|
1529
1522
|
const result = await aiEngine.SimpleLLMCompletion(fullUserPrompt || '', params.contextUser || {}, // Provide empty object if no context user
|
|
@@ -1544,35 +1537,33 @@ class ComponentRunner {
|
|
|
1544
1537
|
success: true,
|
|
1545
1538
|
result: result,
|
|
1546
1539
|
resultObject,
|
|
1547
|
-
modelName: model?.Name || 'Unknown'
|
|
1540
|
+
modelName: model?.Name || 'Unknown',
|
|
1548
1541
|
};
|
|
1549
1542
|
}
|
|
1550
1543
|
catch (error) {
|
|
1551
|
-
(0,
|
|
1544
|
+
(0, global_1.LogError)(error);
|
|
1552
1545
|
return {
|
|
1553
1546
|
success: false,
|
|
1554
1547
|
result: 'Failed to execute prompt: ' + (error instanceof Error ? error.message : String(error)),
|
|
1555
|
-
modelName: ''
|
|
1548
|
+
modelName: '',
|
|
1556
1549
|
};
|
|
1557
1550
|
}
|
|
1558
1551
|
},
|
|
1559
1552
|
EmbedText: async (params) => {
|
|
1560
1553
|
try {
|
|
1561
1554
|
// Handle both single string and array of strings
|
|
1562
|
-
const texts = Array.isArray(params.textToEmbed)
|
|
1563
|
-
? params.textToEmbed
|
|
1564
|
-
: [params.textToEmbed];
|
|
1555
|
+
const texts = Array.isArray(params.textToEmbed) ? params.textToEmbed : [params.textToEmbed];
|
|
1565
1556
|
// Use appropriate embedding model based on size
|
|
1566
1557
|
await aiEngine.Config(false, params.contextUser);
|
|
1567
1558
|
// Get embedding models and filter by size preference
|
|
1568
|
-
const embeddingModels = aiEngine.Models.filter((m) => m.AIModelType === 'Embeddings' &&
|
|
1569
|
-
m.IsActive === true);
|
|
1559
|
+
const embeddingModels = aiEngine.Models.filter((m) => m.AIModelType === 'Embeddings' && m.IsActive === true);
|
|
1570
1560
|
// Select model based on size preference
|
|
1571
1561
|
let model;
|
|
1572
1562
|
if (params.modelSize === 'small') {
|
|
1573
1563
|
// Prefer local/smaller models for 'small'
|
|
1574
|
-
model =
|
|
1575
|
-
embeddingModels.
|
|
1564
|
+
model =
|
|
1565
|
+
embeddingModels.find((m) => m.Vendor === 'LocalEmbeddings') ||
|
|
1566
|
+
embeddingModels.sort((a, b) => (a.PowerRank || 0) - (b.PowerRank || 0))[0];
|
|
1576
1567
|
}
|
|
1577
1568
|
else {
|
|
1578
1569
|
// Use more powerful models for 'medium'
|
|
@@ -1593,21 +1584,19 @@ class ComponentRunner {
|
|
|
1593
1584
|
}
|
|
1594
1585
|
}
|
|
1595
1586
|
// Return single embedding or array based on input
|
|
1596
|
-
const returnEmbeddings = Array.isArray(params.textToEmbed)
|
|
1597
|
-
? embeddings
|
|
1598
|
-
: embeddings[0];
|
|
1587
|
+
const returnEmbeddings = Array.isArray(params.textToEmbed) ? embeddings : embeddings[0];
|
|
1599
1588
|
return {
|
|
1600
1589
|
result: returnEmbeddings,
|
|
1601
1590
|
modelName: model.Name,
|
|
1602
|
-
vectorDimensions: embeddings[0]?.length || 0
|
|
1591
|
+
vectorDimensions: embeddings[0]?.length || 0,
|
|
1603
1592
|
};
|
|
1604
1593
|
}
|
|
1605
1594
|
catch (error) {
|
|
1606
|
-
(0,
|
|
1595
|
+
(0, global_1.LogError)(error);
|
|
1607
1596
|
throw error; // Re-throw for embeddings as they're critical
|
|
1608
1597
|
}
|
|
1609
1598
|
},
|
|
1610
|
-
VectorService: new ai_vectors_memory_1.SimpleVectorService()
|
|
1599
|
+
VectorService: new ai_vectors_memory_1.SimpleVectorService(),
|
|
1611
1600
|
};
|
|
1612
1601
|
}
|
|
1613
1602
|
/**
|
|
@@ -1620,7 +1609,7 @@ class ComponentRunner {
|
|
|
1620
1609
|
// UserInfo is a simple object that can be serialized
|
|
1621
1610
|
const serializedContextUser = JSON.parse(JSON.stringify(options.contextUser));
|
|
1622
1611
|
// utilities - favor the one passed in by the caller, or fall back to the local ones
|
|
1623
|
-
const util = options.utilities || await this.buildLocalMJUtilities(options.contextUser);
|
|
1612
|
+
const util = options.utilities || (await this.buildLocalMJUtilities(options.contextUser));
|
|
1624
1613
|
// Create a lightweight mock metadata object with serializable data
|
|
1625
1614
|
// This avoids authentication/provider issues in the browser context
|
|
1626
1615
|
let entitiesData = [];
|
|
@@ -1655,7 +1644,7 @@ class ComponentRunner {
|
|
|
1655
1644
|
AuditLogTypes: [],
|
|
1656
1645
|
Authorizations: [],
|
|
1657
1646
|
VisibleExplorerNavigationItems: [],
|
|
1658
|
-
AllExplorerNavigationItems: []
|
|
1647
|
+
AllExplorerNavigationItems: [],
|
|
1659
1648
|
};
|
|
1660
1649
|
// Inject both the contextUser and mock metadata into the page
|
|
1661
1650
|
// Playwright only accepts a single argument, so wrap in an object
|
|
@@ -1671,7 +1660,7 @@ class ComponentRunner {
|
|
|
1671
1660
|
// This must be available before any component code runs
|
|
1672
1661
|
if (!window.Metadata) {
|
|
1673
1662
|
window.Metadata = {
|
|
1674
|
-
Provider: mockMd
|
|
1663
|
+
Provider: mockMd,
|
|
1675
1664
|
};
|
|
1676
1665
|
// Created global Metadata mock with Provider (early)
|
|
1677
1666
|
}
|
|
@@ -1742,7 +1731,7 @@ class ComponentRunner {
|
|
|
1742
1731
|
}
|
|
1743
1732
|
catch (error) {
|
|
1744
1733
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1745
|
-
const entities = params.map(p => p.EntityName || 'unknown').join(', ');
|
|
1734
|
+
const entities = params.map((p) => p.EntityName || 'unknown').join(', ');
|
|
1746
1735
|
// Debug logging for errors
|
|
1747
1736
|
if (debug) {
|
|
1748
1737
|
console.log(`❌ RunViews FAILED: Entities=[${entities}]`);
|
|
@@ -1811,7 +1800,7 @@ class ComponentRunner {
|
|
|
1811
1800
|
return {
|
|
1812
1801
|
success: false,
|
|
1813
1802
|
result: errorMessage,
|
|
1814
|
-
modelName: ''
|
|
1803
|
+
modelName: '',
|
|
1815
1804
|
};
|
|
1816
1805
|
}
|
|
1817
1806
|
});
|
|
@@ -1824,9 +1813,7 @@ class ComponentRunner {
|
|
|
1824
1813
|
const paramsWithUser = { ...params, contextUser: options.contextUser };
|
|
1825
1814
|
const result = await util.ai.EmbedText(paramsWithUser);
|
|
1826
1815
|
if (debug) {
|
|
1827
|
-
const count = Array.isArray(result.result)
|
|
1828
|
-
? (Array.isArray(result.result[0]) ? result.result.length : 1)
|
|
1829
|
-
: 1;
|
|
1816
|
+
const count = Array.isArray(result.result) ? (Array.isArray(result.result[0]) ? result.result.length : 1) : 1;
|
|
1830
1817
|
console.log(`🤖 EmbedText SUCCESS: Model="${result.modelName}" Count=${count} Dims=${result.vectorDimensions}`);
|
|
1831
1818
|
}
|
|
1832
1819
|
return result;
|
|
@@ -1849,7 +1836,9 @@ class ComponentRunner {
|
|
|
1849
1836
|
const VectorService = window.MJReactRuntime?.SimpleVectorService ||
|
|
1850
1837
|
class {
|
|
1851
1838
|
// Stub implementation if not available
|
|
1852
|
-
cosineSimilarity(_a, _b) {
|
|
1839
|
+
cosineSimilarity(_a, _b) {
|
|
1840
|
+
return 0;
|
|
1841
|
+
}
|
|
1853
1842
|
};
|
|
1854
1843
|
window.__mjUtilities = {
|
|
1855
1844
|
md: {
|
|
@@ -1861,25 +1850,25 @@ class ComponentRunner {
|
|
|
1861
1850
|
return mockMd.Entities.find((e) => e.Name === name) || null;
|
|
1862
1851
|
},
|
|
1863
1852
|
// Keep async function for GetEntityObject for compatibility
|
|
1864
|
-
GetEntityObject: async (entityName) => await window.__mjGetEntityObject(entityName)
|
|
1853
|
+
GetEntityObject: async (entityName) => await window.__mjGetEntityObject(entityName),
|
|
1865
1854
|
},
|
|
1866
1855
|
rv: {
|
|
1867
1856
|
RunView: async (params) => await window.__mjRunView(params),
|
|
1868
|
-
RunViews: async (params) => await window.__mjRunViews(params)
|
|
1857
|
+
RunViews: async (params) => await window.__mjRunViews(params),
|
|
1869
1858
|
},
|
|
1870
1859
|
rq: {
|
|
1871
|
-
RunQuery: async (params) => await window.__mjRunQuery(params)
|
|
1860
|
+
RunQuery: async (params) => await window.__mjRunQuery(params),
|
|
1872
1861
|
},
|
|
1873
1862
|
ai: {
|
|
1874
1863
|
ExecutePrompt: async (params) => await window.__mjExecutePrompt(params),
|
|
1875
1864
|
EmbedText: async (params) => await window.__mjEmbedText(params),
|
|
1876
|
-
VectorService: new VectorService()
|
|
1877
|
-
}
|
|
1865
|
+
VectorService: new VectorService(),
|
|
1866
|
+
},
|
|
1878
1867
|
};
|
|
1879
1868
|
// Update or create global Metadata mock (in case it wasn't created earlier)
|
|
1880
1869
|
if (!window.Metadata) {
|
|
1881
1870
|
window.Metadata = {
|
|
1882
|
-
Provider: mockMd
|
|
1871
|
+
Provider: mockMd,
|
|
1883
1872
|
};
|
|
1884
1873
|
// Created global Metadata mock with Provider (late)
|
|
1885
1874
|
}
|