@memberjunction/react-test-harness 2.88.0 → 2.90.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/README.md CHANGED
@@ -526,6 +526,104 @@ interface FluentMatcher {
526
526
  }
527
527
  ```
528
528
 
529
+ ## Parallel Testing
530
+
531
+ ### Important: Test Harness Instance Limitations
532
+
533
+ The ReactTestHarness uses a single browser page instance and is **NOT safe for parallel test execution** on the same instance. This is due to Playwright's internal limitations with `exposeFunction` and potential race conditions when multiple tests try to modify the same page context simultaneously.
534
+
535
+ ### Sequential Testing (Single Instance)
536
+
537
+ For sequential test execution, you can safely reuse a single harness instance:
538
+
539
+ ```typescript
540
+ const harness = new ReactTestHarness({ headless: true });
541
+ await harness.initialize();
542
+
543
+ // ✅ CORRECT - Sequential testing on same instance
544
+ for (const test of tests) {
545
+ const result = await harness.testComponent(test.code, test.props);
546
+ // Each test runs one after another, no conflicts
547
+ }
548
+
549
+ await harness.close();
550
+ ```
551
+
552
+ ### Parallel Testing (Multiple Instances)
553
+
554
+ For parallel test execution, you **MUST** create separate ReactTestHarness instances:
555
+
556
+ ```typescript
557
+ // ✅ CORRECT - Parallel testing with separate instances
558
+ const results = await Promise.all(tests.map(async (test) => {
559
+ const harness = new ReactTestHarness({ headless: true });
560
+ await harness.initialize();
561
+
562
+ try {
563
+ return await harness.testComponent(test.code, test.props);
564
+ } finally {
565
+ await harness.close(); // Clean up each instance
566
+ }
567
+ }));
568
+ ```
569
+
570
+ ### Common Mistake to Avoid
571
+
572
+ ```typescript
573
+ // ❌ WRONG - DO NOT DO THIS
574
+ const harness = new ReactTestHarness({ headless: true });
575
+ await harness.initialize();
576
+
577
+ // This will cause conflicts and errors!
578
+ const results = await Promise.all(
579
+ tests.map(test => harness.testComponent(test.code, test.props))
580
+ );
581
+ ```
582
+
583
+ This approach will fail with errors like:
584
+ - "Function '__mjGetEntityObject' has been already registered"
585
+ - "Cannot read properties of undefined (reading 'addBinding')"
586
+
587
+ ### Performance Considerations
588
+
589
+ While creating multiple harness instances has some overhead (each launches its own browser context), the benefits of parallel execution typically outweigh this cost:
590
+
591
+ - **Sequential (1 instance)**: Lower memory usage, but tests run one by one
592
+ - **Parallel (N instances)**: Higher memory usage, but tests complete much faster
593
+
594
+ ### Example: Test Runner with Configurable Parallelism
595
+
596
+ ```typescript
597
+ class TestRunner {
598
+ async runTests(tests: TestCase[], parallel = false) {
599
+ if (parallel) {
600
+ // Create new instance for each test
601
+ return Promise.all(tests.map(async (test) => {
602
+ const harness = new ReactTestHarness({ headless: true });
603
+ await harness.initialize();
604
+ try {
605
+ return await harness.testComponent(test.code, test.props);
606
+ } finally {
607
+ await harness.close();
608
+ }
609
+ }));
610
+ } else {
611
+ // Reuse single instance for all tests
612
+ const harness = new ReactTestHarness({ headless: true });
613
+ await harness.initialize();
614
+
615
+ const results = [];
616
+ for (const test of tests) {
617
+ results.push(await harness.testComponent(test.code, test.props));
618
+ }
619
+
620
+ await harness.close();
621
+ return results;
622
+ }
623
+ }
624
+ }
625
+ ```
626
+
529
627
  ## Usage Examples for TypeScript Projects
530
628
 
531
629
  ### Creating a Reusable Test Utility
@@ -1,3 +1,4 @@
1
+ import { ComponentSpec } from '@memberjunction/interactive-component-types';
1
2
  export interface LintResult {
2
3
  success: boolean;
3
4
  violations: Violation[];
@@ -22,7 +23,7 @@ export interface FixSuggestion {
22
23
  }
23
24
  export declare class ComponentLinter {
24
25
  private static universalComponentRules;
25
- static lintComponent(code: string, componentName: string, componentSpec?: any, isRootComponent?: boolean): Promise<LintResult>;
26
+ static lintComponent(code: string, componentName: string, componentSpec?: ComponentSpec, isRootComponent?: boolean): Promise<LintResult>;
26
27
  private static validateDataRequirements;
27
28
  private static getFunctionName;
28
29
  private static deduplicateViolations;
@@ -1 +1 @@
1
- {"version":3,"file":"component-linter.d.ts","sourceRoot":"","sources":["../../src/lib/component-linter.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAaD,qBAAa,eAAe;IAE1B,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAuyCpC;WAEkB,aAAa,CAC/B,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,aAAa,CAAC,EAAE,GAAG,EACnB,eAAe,CAAC,EAAE,OAAO,GACxB,OAAO,CAAC,UAAU,CAAC;IAuEtB,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAgQvC,OAAO,CAAC,MAAM,CAAC,eAAe;IA2B9B,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAyBpC,OAAO,CAAC,MAAM,CAAC,sBAAsB;CA6pBtC"}
1
+ {"version":3,"file":"component-linter.d.ts","sourceRoot":"","sources":["../../src/lib/component-linter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAE5E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAaD,qBAAa,eAAe;IAE1B,OAAO,CAAC,MAAM,CAAC,uBAAuB,CA2pCpC;WAEkB,aAAa,CAC/B,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,aAAa,CAAC,EAAE,aAAa,EAC7B,eAAe,CAAC,EAAE,OAAO,GACxB,OAAO,CAAC,UAAU,CAAC;IAuEtB,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAgQvC,OAAO,CAAC,MAAM,CAAC,eAAe;IA2B9B,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAyBpC,OAAO,CAAC,MAAM,CAAC,sBAAsB;CA6pBtC"}