dlest
Version:
Jest for your data layer - test runner for analytics tracking implementations
286 lines (237 loc) • 7.09 kB
JavaScript
const fs = require('fs');
const path = require('path');
const defaults = require('./defaults');
/**
* Configuration Loader
*
* Loads and merges DLest configuration from various sources
*/
class ConfigLoader {
constructor(cwd = process.cwd()) {
this.cwd = cwd;
this.configCache = null;
}
/**
* Load configuration with precedence:
* 1. Command line options
* 2. dlest.config.js
* 3. package.json dlest field
* 4. Defaults
*/
load(cliOptions = {}) {
if (this.configCache && !cliOptions.force) {
return this.configCache;
}
const config = {
...defaults,
...this.loadFromPackageJson(),
...this.loadFromConfigFile(),
...this.loadFromEnvironment(),
...cliOptions,
};
// Resolve paths relative to project root
config.testDir = path.resolve(this.cwd, config.testDir);
// Ensure testMatch is an array
if (typeof config.testMatch === 'string') {
config.testMatch = [config.testMatch];
}
// Validate configuration
this.validate(config);
this.configCache = config;
return config;
}
/**
* Load configuration from package.json
*/
loadFromPackageJson() {
const packageJsonPath = path.join(this.cwd, 'package.json');
try {
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
return packageJson.dlest || {};
}
} catch (error) {
console.warn(`Warning: Could not parse package.json: ${error.message}`);
}
return {};
}
/**
* Load configuration from environment variables
*/
loadFromEnvironment() {
const envConfig = {};
// Base URL from environment
if (process.env.DLEST_BASE_URL) {
envConfig.baseURL = process.env.DLEST_BASE_URL;
}
// Authentication from environment
if (process.env.DLEST_AUTH_USER && process.env.DLEST_AUTH_PASS) {
envConfig.auth = {
username: process.env.DLEST_AUTH_USER,
password: process.env.DLEST_AUTH_PASS
};
}
// Browser from environment
if (process.env.DLEST_BROWSER) {
envConfig.browser = process.env.DLEST_BROWSER;
}
// Headless mode from environment
if (process.env.DLEST_HEADLESS !== undefined) {
envConfig.headless = process.env.DLEST_HEADLESS !== 'false';
}
// Timeout from environment
if (process.env.DLEST_TIMEOUT) {
envConfig.timeout = parseInt(process.env.DLEST_TIMEOUT);
}
// CI mode from environment
if (process.env.CI === 'true') {
envConfig.ci = true;
envConfig.headless = true;
}
return envConfig;
}
/**
* Load configuration from dlest.config.js
*/
loadFromConfigFile() {
const configPaths = [
'dlest.config.js',
'dlest.config.mjs',
'.dlest.config.js',
'config/dlest.js',
];
for (const configPath of configPaths) {
const fullPath = path.join(this.cwd, configPath);
try {
if (fs.existsSync(fullPath)) {
// Clear require cache to allow config reloading
delete require.cache[path.resolve(fullPath)];
const config = require(fullPath);
// Handle both module.exports and export default
return typeof config === 'function' ? config() : config;
}
} catch (error) {
throw new Error(`Error loading config file ${configPath}: ${error.message}`);
}
}
return {};
}
/**
* Validate configuration
*/
validate(config) {
// Validate browsers
const validBrowsers = ['chromium', 'firefox', 'webkit', 'chrome', 'safari'];
if (!Array.isArray(config.browsers)) {
config.browsers = [config.browsers];
}
config.browsers.forEach(browser => {
if (!validBrowsers.includes(browser.toLowerCase())) {
throw new Error(`Invalid browser: ${browser}. Valid browsers: ${validBrowsers.join(', ')}`);
}
});
// Validate timeout
if (config.timeout <= 0) {
throw new Error('Timeout must be greater than 0');
}
// Validate test directory
if (!fs.existsSync(config.testDir)) {
console.warn(`Warning: Test directory ${config.testDir} does not exist`);
}
// Validate data layer settings
if (!config.dataLayer.variableName) {
throw new Error('Data layer variable name cannot be empty');
}
if (config.dataLayer.waitTimeout <= 0) {
throw new Error('Data layer wait timeout must be greater than 0');
}
return true;
}
/**
* Get test files based on configuration
*/
getTestFiles(config, patterns = []) {
const glob = require('glob');
// If specific patterns provided, use those
if (patterns.length > 0) {
const files = [];
patterns.forEach(pattern => {
try {
const matches = glob.sync(pattern, {
cwd: this.cwd,
absolute: true
});
files.push(...matches);
} catch (error) {
throw new Error(`Error finding test files with pattern ${pattern}: ${error.message}`);
}
});
return [...new Set(files)]; // Remove duplicates
}
// Use config patterns
const files = [];
config.testMatch.forEach(pattern => {
try {
const matches = glob.sync(pattern, {
cwd: config.testDir,
absolute: true
});
files.push(...matches);
} catch (error) {
throw new Error(`Error finding test files with pattern ${pattern}: ${error.message}`);
}
});
return files;
}
/**
* Create example config file
*/
createExampleConfig(configPath = 'dlest.config.js') {
const exampleConfig = `// DLest Configuration
module.exports = {
// Base URL for tests
baseURL: 'http://localhost:3000',
// Browser settings
browsers: ['chromium'], // chromium, firefox, webkit
headless: true,
// Test settings
timeout: 30000,
testDir: './tests',
testMatch: ['**/*.test.js'],
// Data layer settings
dataLayer: {
variableName: 'dataLayer', // Custom data layer variable name
waitTimeout: 5000, // How long to wait for events
},
// Development server settings
serve: {
root: './fixtures', // Serve HTML fixtures from this directory
port: 3000, // Default port for dev server
},
// Output settings
verbose: false,
// Reporting
reporter: 'default', // default, json, junit
// Playwright specific settings
playwright: {
viewport: { width: 1280, height: 720 },
locale: 'en-US',
timezone: 'America/New_York',
}
};
`;
const fullPath = path.join(this.cwd, configPath);
if (fs.existsSync(fullPath)) {
throw new Error(`Configuration file ${configPath} already exists`);
}
fs.writeFileSync(fullPath, exampleConfig, 'utf8');
return fullPath;
}
/**
* Clear configuration cache
*/
clearCache() {
this.configCache = null;
}
}
module.exports = { ConfigLoader };