Creating custom modules for MAF is straightforward when you follow the established patterns. This guide provides step-by-step instructions for creating a new module that integrates seamlessly with the MAF ecosystem.
A typical MAF module follows this directory structure:
packages/your-module/
├── package.json # Module configuration and dependencies
├── index.js # Main entry point that imports step definitions
├── README.md # Installation and usage documentation
├── LICENSE # License file (typically Apache-2.0)
├── CHANGELOG.md # Version history
├── stepDefinitions/ # Cucumber step definitions
│ └── yourSteps.js
├── features/ # Feature files for testing your module
│ └── *.feature
└── test/ # Test utilities and setup
└── report/ # Generated test reports
-
Create your module directory:
mkdir packages/your-module cd packages/your-module -
Initialize package.json:
{ "name": "@ln-maf/your-module", "publishConfig": { "access": "public" }, "version": "1.0.0", "description": "Description of your MAF module", "main": "index.js", "scripts": { "test": "mkdir -p ../../test/report && npx cucumber-js -f json:../../test/report/your-module.json --require \"stepDefinitions/*.js\" features/$FEATURE_FILE", "test:coverage": "mkdir -p ../../test/report && npx nyc --reporter=lcov --reporter=text --report-dir=../../coverage npx cucumber-js -f json:../../test/report/your-module.json --require \"stepDefinitions/*.js\" features/$FEATURE_FILE" }, "license": "Apache-2.0", "dependencies": { "dependency1": "^1.0.0" }, "peerDependencies": { "@cucumber/cucumber": ">= 12.0.0", "@ln-maf/core": ">= 4.0.0" }, "devDependencies": { "@cucumber/cucumber": "^12.0.0", "@ln-maf/core": "file:../core", "@ln-maf/validations": "file:../validations", "multiple-cucumber-html-reporter": "3.7.0", "nyc": "^17.0.0" }, "repository": { "type": "git", "url": "git+https://github.com/hpcc-systems/MAF.git" }, "keywords": [ "cucumber-js", "testing", "gherkin", "cucumber-steps" ], "bugs": { "url": "https://github.com/hpcc-systems/MAF/issues" } }
Create index.js that imports your step definitions:
require('./stepDefinitions/yourSteps.js')Create stepDefinitions/yourSteps.js following MAF patterns:
const { MAFWhen, MAFSave, performJSONObjectTransform, fillTemplate } = require('@ln-maf/core')
const { Given, When, Then, setDefaultTimeout } = require('@cucumber/cucumber')
// Set a reasonable timeout for your operations
setDefaultTimeout(30 * 1000)
// Example: Given step for configuration
Given('my service is configured with {jsonObject}', async function (config) {
// Use performJSONObjectTransform to handle template variables
const processedConfig = performJSONObjectTransform(config, this.results)
// Store configuration for later use
this.results.serviceConfig = processedConfig
})
// Example: When step that performs an action and stores results
MAFWhen('I perform action with {jsonObject}', async function (actionData) {
// Process the input data
const processedData = performJSONObjectTransform(actionData, this.results)
// Perform your operation
const result = await performYourOperation(processedData)
// Store the result for use in subsequent steps
this.results.lastRun = result
this.results.actionResult = result
return result
})
// Example: Helper function for your operations
async function performYourOperation(data) {
// Your implementation here
return { success: true, data: data }
}Create features/yourModule.feature to test your steps:
Feature: Your Module Testing
Background:
Given my service is configured with:
"""
{
"endpoint": "http://localhost:3000",
"timeout": 5000
}
"""
Scenario: Basic functionality test
When I perform action with:
"""
{
"action": "test",
"data": "sample"
}
"""
Then item "lastRun.success" is equal to true
And item "lastRun.data.action" is equal to "test"
Scenario: Template variable usage
When set "testValue" to "dynamic_data"
And I perform action with:
"""
{
"action": "template_test",
"data": "${testValue}"
}
"""
Then item "lastRun.data.data" is equal to "dynamic_data"Create a comprehensive README.md:
# Your Module Name
Brief description of what your module does and which systems/services it integrates with.
[![npm package][npm-image]][npm-url]
[](https://github.com/hpcc-systems/MAF/actions/workflows/package-your-module.yml)
## Installation
```bash
npm install @ln-maf/your-moduleAdd the module to your step definitions file:
require('@ln-maf/your-module')If your module requires configuration, explain how to set it up here.
Given my service is configured with {jsonObject}- Configures the service
When I perform action with {jsonObject}- Performs an action
- Use standard validation steps from
@ln-maf/validations
Feature: Example Usage
Scenario: Basic usage
Given my service is configured with:
"""
{
"setting": "value"
}
"""
When I perform action with:
"""
{
"action": "test"
}
"""
Then item "lastRun.success" is equal to true-
Store results consistently:
// Always store the main result in lastRun for template access this.results.lastRun = result // Store in named properties for specific access this.results.myModuleResult = result
-
Use MAFWhen for operations that return data:
MAFWhen('I do something', async function() { const result = await doSomething() this.results.lastRun = result return result })
-
Always use performJSONObjectTransform:
const processedData = performJSONObjectTransform(inputData, this.results)
-
Handle template variables in strings:
const processedString = fillTemplate(inputString, this.results)
MAFWhen('I perform risky operation', async function() {
try {
const result = await riskyOperation()
this.results.lastRun = result
return result
} catch (error) {
this.results.lastError = error.message
throw error
}
})-
Test locally:
cd packages/your-module npm test
-
Test with coverage:
npm run test:coverage
-
Test from MAF root:
npm test -w packages/your-module
- Update main package.json: Add your module to the workspace configuration
- Add CI/CD: Create
.github/workflows/package-your-module.yml - Update main README: Add your module to the available modules list
- Add to lerna: Your module will be automatically included in Lerna operations
Once your module is ready:
- Test thoroughly: Ensure all tests pass
- Update CHANGELOG.md: Document your changes
- Version appropriately: Follow semantic versioning
- Publish to npm: Use Lerna for coordinated releases
# From MAF root
lerna publish