# Testing & Build System

## Overview

The CG Mobile App metadata contracts are tested using Jest with custom transformers and built using the Modeler CLI plugin. The testing framework supports business logic validation, while the build system compiles metadata contracts into runtime artifacts (RTAS).

---

## Build System

### Build Commands

```bash
# Shipped by the Modeler CLI plugin — long form and `sf mdl` shortcuts
sf modeler workspace build          # or `sf mdl build`     — full workspace compile
sf modeler workspace validate       # or `sf mdl validate`  — validate XML against XSDs
sf modeler workspace cleanup        # or `sf mdl clean`     — clean compiled artifacts
sf modeler workspace package        # or `sf mdl package`   — package for deployment
sf modeler workspace server start   # or `sf mdl simulate`  — start the Modeler development server
```

### Build Output

**Location:** `appl/build/`

The build process compiles all XML contracts (DataSource, BusinessObject, ListObject, Process, UI) into a single `rtas.json` runtime artifact (~10MB) that the mobile framework consumes.

### Build Process Flow

```
src/**/*.xml contracts
    |
    ↓ sf modeler workspace build
    |
Validation (XSD schema checking)
    |
    ↓
Compilation (XML → JSON transformation)
    |
    ↓
appl/build/rtas.json (Runtime Artifact)
    |
    ↓
Mobile Framework loads rtas.json
```

---

## Jest Test Framework

### Test Organization

Tests are organized under `test/unitTests/` mirroring the source structure:

```
test/
├── helpers/                    # Test utilities
│   ├── FWEngineLoader.js      # Loads @ind-rcg/framework
│   ├── RTASLoader.js          # Loads compiled rtas.json
│   ├── ConstantsMock.js       # Mock constants (STATE, MIN_DATE, BLConstants)
│   ├── CPMock.js              # Context Provider mock
│   └── utils.js               # Test utilities
├── setupFiles/
│   └── commonTestSetup.js     # Jest setup (extends matchers, sets globals)
├── transformers/
│   └── exportModelerMethodTransformer.js  # Custom .bl.js transformer
└── unitTests/                 # Test files organized by module
    ├── Analytics and Reporting/
    ├── Asset Management/
    ├── Call/
    ├── Visit/
    └── ...
```

### Test Naming Convention

Tests mirror the source file structure with `.test.js` suffix:

-   Source: `src/Asset Management/BO/BoFlyoutRegisterAsset/Mv2/BoFlyoutRegisterAsset.ValidateLength.bl.js`
-   Test: `test/unitTests/Asset Management/BO/BoFlyoutRegisterAsset/Mv2/BoFlyoutRegisterAsset.ValidateLength.bl.test.js`

---

## Custom .bl.js Transformer

**Location:** `test/transformers/exportModelerMethodTransformer.js`

### Purpose

Business logic `.bl.js` files don't use standard module exports. The custom transformer:

1. Extracts function name from `@function` JSDoc tag
2. Generates `module.exports` statement
3. Auto-generates return statements from `@returns` JSDoc tag
4. Generates source maps for debugging

### Transformation Example

**Original `.bl.js` file:**

```javascript
/**
 * @function validateLength
 * @this BoFlyoutRegisterAsset
 * @param {Object} messageCollector
 */
function validateLength(messageCollector) {
    var serialNumber = this.getSerialNumber();
    if (serialNumber && serialNumber.length > 50) {
        messageCollector.add({
            level: 'error',
            objectClass: 'BoFlyoutRegisterAsset',
            messageID: 'CasAstRegisterSerialNumberTooLong',
        });
    }
}
```

**Transformed output (by Jest transformer):**

```javascript
// ... original code ...
/* Generated */ /* istanbul ignore next */ module.exports = { validateLength };
```

### Key Features

-   **Function Export:** Regex `/\s*\*\s\@function\s(.+)$/m` extracts name from JSDoc
-   **Return Statement:** Regex `/\s*\*\s\@returns\s(.+)$/m` extracts return variable
-   **Source Maps:** Maps transformed code back to original for stack traces

---

## Test Helper Components

### FWEngineLoader.js

Loads the CG Cloud Framework Engine for testing:

```javascript
const { FWEngine } = require('../helpers/FWEngineLoader.js');

// Initialize with RTAS metadata
FWEngine.AppManager.initForModeler(RTAS, {}, true, () => {});
```

### RTASLoader.js

Loads the compiled RTAS artifact (requires a prior `sf modeler workspace build`):

```javascript
const { RTAS } = require('../helpers/RTASLoader.js');
```

### ConstantsMock.js

Provides mock constants for business logic testing:

```javascript
const { STATE, MIN_DATE, BLConstants } = require('../helpers/ConstantsMock.js');
```

### CPMock.js (Context Provider Mock)

Mocks the framework's Context Provider for unit tests:

```javascript
const { CPMock } = require('../helpers/CPMock.js');
```

---

## Writing Tests

### Standard Test Pattern

```javascript
const { FWEngine } = require('../../../../../helpers/FWEngineLoader.js');
const { RTAS } = require('../../../../../helpers/RTASLoader.js');

describe('BoObjectName.methodName', () => {
    const { methodName } = require('../../../../../../src/Module/BO/BoObjectName/Mv2/BoObjectName.MethodName.bl.js');

    let boInstance;
    let methodBounded;

    beforeAll(() => {
        FWEngine.AppManager.initForModeler(RTAS, {}, true, () => {});
    });

    beforeEach(() => {
        // Instantiate BO from framework
        boInstance = FWEngine.BoFactory.instantiate('BoObjectName');

        // Bind function to BO instance (mimics framework behavior)
        methodBounded = methodName.bind(boInstance);
    });

    afterEach(() => {
        jest.clearAllMocks();
        FWEngine.ApplicationContext.__reset();
    });

    it('should handle expected case', () => {
        // Arrange
        boInstance.setPropertyName('testValue');

        // Act
        const result = methodBounded();

        // Assert
        expect(result).toBe(expectedValue);
    });

    it('should handle edge case', () => {
        // Arrange
        boInstance.setPropertyName('');

        // Act
        const result = methodBounded();

        // Assert
        expect(result).toBeUndefined();
    });
});
```

### Testing Validation Methods

```javascript
it('should add error when validation fails', () => {
    // Arrange
    const messageCollector = { add: jest.fn() };
    boInstance.setFieldValue('invalid');

    // Act
    validateMethodBounded(messageCollector);

    // Assert
    expect(messageCollector.add).toHaveBeenCalledWith({
        level: 'error',
        objectClass: 'BoObjectName',
        messageID: 'ValidationErrorId',
    });
});
```

### Testing Async Methods

```javascript
it('should load data asynchronously', async () => {
    // Arrange
    const params = { customerPKey: 'test-key' };

    // Act
    const result = await asyncMethodBounded(params);

    // Assert
    expect(result).toBeDefined();
    expect(result.getItems().length).toBeGreaterThan(0);
});
```

---

## Recommended Coverage Baseline

Coverage thresholds are workspace-configurable in `jest.config.js`, not
enforced by the shipped CLI. The values below are a recommended baseline
matching what a fresh CG Mobile workspace ships with — your workspace's
`jest.config.js` is the source of truth.

| Metric     | Recommended baseline | Description               |
| ---------- | -------------------- | ------------------------- |
| Statements | 70%                  | All executable statements |
| Branches   | 65%                  | All conditional branches  |
| Functions  | 70%                  | All declared functions    |
| Lines      | 70%                  | All source lines          |

### Running Coverage

Test execution is workspace-driven, not part of the shipped CLI. Run Jest
directly (or via any wrapper script your workspace's `package.json` defines):

```bash
jest                    # Run tests with coverage per jest.config.js
jest --no-coverage      # Faster iteration without coverage
```

Coverage reports generated in `coverage/` directory with `lcov` format.

---

## Jest Configuration

**Location:** `jest.config.js`

### Key Settings

```javascript
const config = {
    clearMocks: true,
    resetMocks: true,
    restoreMocks: true,

    collectCoverage: true,
    collectCoverageFrom: ['src/**/*.js'],
    coverageDirectory: 'coverage',
    coverageProvider: 'babel',

    setupFilesAfterEnv: ['<rootDir>/test/setupFiles/commonTestSetup.js'],
    testEnvironment: 'jsdom',

    // Custom transformer for .bl.js files
    transform: {
        '.bl.js$': '<rootDir>/test/transformers/exportModelerMethodTransformer.js',
    },
};
```

---

## Build Validation

### XSD Schema Validation

The build validates all XML contracts against XSD schemas:

```bash
sf modeler workspace validate
```

Validates:

-   `.datasource.xml` files against DataSource XSD
-   `.businessobject.xml` files against BusinessObject XSD
-   `.listobject.xml` / `.listitem.xml` against ListObject XSD
-   `.processflow.xml` files against Process XSD
-   `.userinterface.xml` files against UI XSD

### Feature Flags

Feature flags in `branchConfig.json` control validation behavior:

```json
{
    "features": [
        {
            "key": "USE_LATEST_XSD_VALIDATIONS",
            "value": false,
            "description": "Use latest XSD files for validation"
        }
    ]
}
```

---

## Development Server

### Starting the Server

```bash
sf modeler workspace server start
```

-   Serves compiled workspace for browser testing
-   Default port: 3000
-   Hot reload on contract changes
-   Useful for visual UI testing

---

## Best Practices

1. **Test before commit:** Always run `jest` (or your workspace's test
   wrapper) before pushing changes.
2. **Build first:** Compile the workspace with `sf modeler workspace build`
   (or the shortcut `sf mdl build`) before running tests — tests typically load
   the compiled RTAS artifact.
3. **Match source structure:** Place test files mirroring `src/` path
4. **Use .bind():** Always bind .bl.js functions to a BO instance
5. **Reset state:** Use `afterEach` to clear mocks and reset ApplicationContext
6. **Mock dependencies:** Use ConstantsMock, CPMock for framework dependencies
7. **Cover edge cases:** Test null/undefined/empty string inputs

---

_This documentation is maintained by the Modeler CLI plugin and refreshed on workspace upgrade._
