@tywalk/pcf-helper
Version:
Command line helper for building and publishing PCF controls to Dataverse.
317 lines (316 loc) • 15.8 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const session_pcf_1 = require("../tasks/session-pcf");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const color_logger_1 = __importDefault(require("@tywalk/color-logger"));
jest.mock('fs');
jest.mock('path');
jest.mock('@tywalk/color-logger', () => ({
__esModule: true,
default: {
log: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn()
}
}));
const mockFs = fs;
const mockPath = path;
describe('session-pcf loadConfig', () => {
beforeEach(() => {
jest.clearAllMocks();
delete process.env.REMOTE_ENVIRONMENT_URL;
delete process.env.REMOTE_SCRIPT_TO_INTERCEPT;
delete process.env.REMOTE_STYLESHEET_TO_INTERCEPT;
delete process.env.LOCAL_CSS_PATH;
delete process.env.LOCAL_BUNDLE_PATH;
delete process.env.START_WATCH;
mockPath.join.mockImplementation((...args) => args.join('/'));
jest.spyOn(process, 'cwd').mockReturnValue('/test/cwd');
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('config file loading', () => {
it('should load config from default session.config.json', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
remoteScriptToIntercept: '/static/bundle.js',
remoteStylesheetToIntercept: '/static/style.css',
localBundlePath: '/local/bundle.js',
localCssPath: '/local/style.css'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteEnvironmentUrl).toBe('https://example.crm.dynamics.com');
expect(mockFs.existsSync).toHaveBeenCalled();
expect(mockFs.readFileSync).toHaveBeenCalled();
});
it('should load config from custom config file path', () => {
const config = {
remoteEnvironmentUrl: 'https://custom.crm.dynamics.com',
remoteScriptToIntercept: 'custom-bundle.js',
localBundlePath: '/custom/bundle.js',
localCssPath: '/custom/style.css'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)('custom.config.json');
expect(result.remoteEnvironmentUrl).toBe('https://custom.crm.dynamics.com');
});
it('should return empty object if config file does not exist', () => {
mockFs.existsSync.mockReturnValue(false);
const result = (0, session_pcf_1.loadConfig)();
expect(result).toEqual({});
});
it('should log error if config file is invalid JSON', () => {
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue('{ invalid json }');
const result = (0, session_pcf_1.loadConfig)();
expect(color_logger_1.default.error).toHaveBeenCalledWith(expect.stringContaining('Failed to parse config file'));
expect(result).toEqual({});
});
it('should log warning if config file not found and no env vars', () => {
mockFs.existsSync.mockReturnValue(false);
(0, session_pcf_1.loadConfig)();
expect(color_logger_1.default.warn).not.toHaveBeenCalled(); // Returns empty, no warning since it checks for env vars
});
it('should log config when file is loaded successfully', () => {
const config = { remoteEnvironmentUrl: 'https://example.crm.dynamics.com' };
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
(0, session_pcf_1.loadConfig)();
expect(color_logger_1.default.log).toHaveBeenCalledWith(expect.stringContaining('Loaded config file'));
});
});
describe('environment variable overrides', () => {
it('should use REMOTE_ENVIRONMENT_URL env var if set', () => {
process.env.REMOTE_ENVIRONMENT_URL = 'https://env.crm.dynamics.com';
mockFs.existsSync.mockReturnValue(false);
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteEnvironmentUrl).toBe('https://env.crm.dynamics.com');
});
it('should use REMOTE_SCRIPT_TO_INTERCEPT env var if set', () => {
process.env.REMOTE_ENVIRONMENT_URL = 'https://env.crm.dynamics.com';
process.env.REMOTE_SCRIPT_TO_INTERCEPT = 'https://example.com/env-bundle.js';
mockFs.existsSync.mockReturnValue(false);
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteScriptToIntercept).toBe('https://example.com/env-bundle.js');
});
it('should use LOCAL_CSS_PATH env var if set', () => {
process.env.REMOTE_ENVIRONMENT_URL = 'https://env.crm.dynamics.com';
process.env.LOCAL_CSS_PATH = '/env/style.css';
mockFs.existsSync.mockReturnValue(false);
const result = (0, session_pcf_1.loadConfig)();
expect(result.localCssPath).toBe('/env/style.css');
});
it('should use LOCAL_BUNDLE_PATH env var if set', () => {
process.env.REMOTE_ENVIRONMENT_URL = 'https://env.crm.dynamics.com';
process.env.LOCAL_BUNDLE_PATH = '/env/bundle.js';
mockFs.existsSync.mockReturnValue(false);
const result = (0, session_pcf_1.loadConfig)();
expect(result.localBundlePath).toBe('/env/bundle.js');
});
it('should prefer env var over config file value', () => {
const config = {
remoteEnvironmentUrl: 'https://config.crm.dynamics.com'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
process.env.REMOTE_ENVIRONMENT_URL = 'https://env.crm.dynamics.com';
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteEnvironmentUrl).toBe('https://env.crm.dynamics.com');
});
});
describe('URL normalization and path construction', () => {
it('should construct full URL from base URL and relative script path', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
remoteScriptToIntercept: 'webresources/bundle.js'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteScriptToIntercept).toBe('https://example.crm.dynamics.com/webresources/bundle.js');
});
it('should handle script path that already starts with /', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
remoteScriptToIntercept: '/webresources/bundle.js'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteScriptToIntercept).toBe('https://example.crm.dynamics.com/webresources/bundle.js');
});
it('should not add duplicate slashes when base URL has trailing slash', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com/',
remoteScriptToIntercept: '/webresources/bundle.js'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteScriptToIntercept).toBe('https://example.crm.dynamics.com/webresources/bundle.js');
expect(result.remoteScriptToIntercept).not.toContain('//webresources');
});
it('should keep full URLs unchanged', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
remoteScriptToIntercept: 'https://cdn.example.com/bundle.js'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteScriptToIntercept).toBe('https://cdn.example.com/bundle.js');
});
it('should handle stylesheet path normalization', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
remoteStylesheetToIntercept: 'webresources/style.css'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteStylesheetToIntercept).toBe('https://example.crm.dynamics.com/webresources/style.css');
});
it('should not attempt URL construction if base URL is missing', () => {
const config = {
remoteScriptToIntercept: 'webresources/bundle.js'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
// Should remain as is since no base URL
expect(result.remoteScriptToIntercept).toBe('webresources/bundle.js');
});
it('should not attempt URL construction if script is empty', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
remoteScriptToIntercept: ''
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
// Should remain empty
expect(result.remoteScriptToIntercept).toBe('');
});
});
describe('watch flag handling', () => {
it('should set startWatch to true if config has it', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
startWatch: true
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.startWatch).toBe(true);
});
it('should set startWatch to false if config has it false', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
startWatch: false
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.startWatch).toBe(false);
});
it('should check START_WATCH env var', () => {
process.env.REMOTE_ENVIRONMENT_URL = 'https://env.crm.dynamics.com';
process.env.START_WATCH = 'true';
mockFs.existsSync.mockReturnValue(false);
const result = (0, session_pcf_1.loadConfig)();
expect(result.startWatch).toBe(true);
});
it('should set startWatch to false if START_WATCH env var is not true', () => {
process.env.REMOTE_ENVIRONMENT_URL = 'https://env.crm.dynamics.com';
process.env.START_WATCH = 'false';
mockFs.existsSync.mockReturnValue(false);
const result = (0, session_pcf_1.loadConfig)();
expect(result.startWatch).toBe(false);
});
it('should default startWatch to false', () => {
process.env.REMOTE_ENVIRONMENT_URL = 'https://env.crm.dynamics.com';
mockFs.existsSync.mockReturnValue(false);
const result = (0, session_pcf_1.loadConfig)();
expect(result.startWatch).toBe(false);
});
});
describe('edge cases', () => {
it('should handle config with only some fields present', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteEnvironmentUrl).toBe('https://example.crm.dynamics.com');
expect(result.remoteScriptToIntercept).toBeUndefined();
});
it('should handle empty config file', () => {
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue('{}');
const result = (0, session_pcf_1.loadConfig)();
expect(result).toEqual({
remoteEnvironmentUrl: undefined,
remoteScriptToIntercept: undefined,
remoteStylesheetToIntercept: undefined,
localCssPath: undefined,
localBundlePath: undefined,
startWatch: false,
watchRetry: true
});
});
it('should handle config file with extra fields', () => {
const config = {
remoteEnvironmentUrl: 'https://example.crm.dynamics.com',
extraField: 'should be ignored'
};
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify(config));
const result = (0, session_pcf_1.loadConfig)();
expect(result.remoteEnvironmentUrl).toBe('https://example.crm.dynamics.com');
expect(result.watchRetry).toBe(true);
});
});
});