External LibrariesIntegration with Web Components

Integration with Web Components

External libraries work alongside components created in the ReAPI web editor.

Loading Order

Scripts load in this sequence:

1. Built-in Libraries
   └── Lodash, Faker, Zod, Ky, Luxon (always available)

2. External Libraries (your code)
   └── Loaded by URL, in registration order

3. Global Scripts (web editor)
   └── Can call external library functions

4. Inline Code (node-level)
   └── Can call everything above

Key insight: External libraries load first, so web-created scripts can use your functions.

Calling External Functions from Web Editor

Once your library is synced, use it in global scripts:

// Global script in web editor
class $$PaymentUtils {
  static validateCard(cardNumber) {
    // Use external library function
    return MyTestLib.validators.isValidCreditCard(cardNumber);
  }
 
  static generateTestPayment() {
    // Use external library generator
    return MyTestLib.generatePaymentData({
      amount: _.random(100, 1000),
      currency: "USD",
    });
  }
}

Calling Web Functions from External Library

Your external library can call global scripts that loaded before inline code:

// In external library
function: async () => {
  // ✅ Can use built-in libraries
  const id = faker.string.uuid();
 
  // ✅ Can use other external library functions
  const hash = MyTestLib.utils.sha256(data);
 
  // ❌ Cannot directly call web-created global scripts
  // (they load after external libraries)
  // $$WebUtils.doSomething(); // Won't work
}

Workaround: Pass web-created utilities via context:

// In web global script (loads after external lib)
$context.webUtils = {
  formatCurrency: (amount) => $$Formatters.currency(amount),
  validateOrder: (order) => $$Validators.order(order),
};
// In external test case
function: async () => {
  // Access via context
  const formatted = $context.webUtils?.formatCurrency(100);
}

Namespace Conventions

Avoid conflicts with consistent naming:

SourceConventionExample
External LibraryLibName.functionMyTestLib.validate()
Web Global Script$$ClassName.method$$Utils.format()
Built-inGlobal namefaker.internet.email()
// Clear, no conflicts
const hash = MyTestLib.hashGenerator.sha256(data);    // External
const formatted = $$Formatters.currency(amount);       // Web
const email = faker.internet.email();                  // Built-in

Sharing Schemas

Define schemas in external library, use everywhere:

// src/schemas/index.ts (external library)
export const Schemas = {
  user: z.object({
    id: z.number(),
    email: z.string().email(),
    role: z.enum(["admin", "user"]),
  }),
 
  order: z.object({
    id: z.string().uuid(),
    total: z.number().positive(),
    status: z.enum(["pending", "paid", "shipped"]),
  }),
};
 
// Export for global access
export const $$Schemas = Schemas;
// In web global script
const userResult = MyTestLib.$$Schemas.user.safeParse(data);
 
// In web inline code
const isValid = MyTestLib.$$Schemas.order.safeParse($response.body).success;

Migration: Web Editor → External Library

Move complex logic from web editor to external library:

Before (Web Editor)

// Complex global script in web editor
class $$PaymentValidator {
  static isValidCard(number) {
    // Luhn algorithm implementation
    let sum = 0;
    let isEven = false;
    for (let i = number.length - 1; i >= 0; i--) {
      let digit = parseInt(number[i], 10);
      if (isEven) {
        digit *= 2;
        if (digit > 9) digit -= 9;
      }
      sum += digit;
      isEven = !isEven;
    }
    return sum % 10 === 0;
  }
 
  // More complex methods...
}

After (External Library)

// src/validators/payment.ts
export const isValidCard: AssertionFunction = {
  id: "is-valid-card",
  name: "Is Valid Card",
  enabled: true,
  deprecated: false,
  tested: true,
  function: async (number: string) => {
    // Same logic, but now:
    // - Version controlled
    // - Unit tested
    // - TypeScript typed
    const isValid = luhnCheck(number);
    
    $addAssertionResult({
      passed: isValid,
      message: isValid ? "Valid card number" : "Invalid card number",
      operator: "isValidCard",
      leftValue: number,
      rightValue: "valid card",
    });
  },
};
 
function luhnCheck(number: string): boolean {
  // Implementation with tests
}
// Web editor now just references external
class $$PaymentValidator {
  static isValidCard(number) {
    // Delegate to external library
    return MyTestLib.validators.isValidCard(number);
  }
}

Hybrid Workflow

Recommended approach for teams:

Component TypeWhere to CreateWhy
Simple assertionsWeb editorQuick to create
Complex assertionsExternal libraryNeeds testing
Quick generatorsWeb editorOne-off use
Reusable generatorsExternal libraryShared across projects
Simple hooksWeb editorBasic auth/logging
Complex hooksExternal libraryEncryption, signing
Visual testsWeb editorQA creates these
Code-driven testsExternal libraryDevelopers create these

Deprecation Strategy

When moving from web to external:

  1. Create external version with same functionality
  2. Mark web version as deprecated (still works, hidden from UI)
  3. Update existing tests to use external version
  4. Disable web version after migration complete
// Web version - mark deprecated
{
  id: "old-validator",
  enabled: true,
  deprecated: true,  // Hidden from UI, still works
  // ...
}