UNPKG

@tywalk/pcf-helper

Version:

Command line helper for building and publishing PCF controls to Dataverse.

250 lines (249 loc) 11.8 kB
"use strict"; 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 init_pcf_1 = require("../tasks/init-pcf"); const child_process_1 = require("child_process"); const fs = __importStar(require("fs")); const color_logger_1 = __importDefault(require("@tywalk/color-logger")); jest.mock('child_process'); jest.mock('fs'); jest.mock('@tywalk/color-logger', () => ({ __esModule: true, default: { log: jest.fn(), success: jest.fn(), error: jest.fn(), warn: jest.fn(), debug: jest.fn() } })); const mockSpawnSync = child_process_1.spawnSync; const mockFs = fs; describe('init-pcf task', () => { beforeEach(() => { jest.clearAllMocks(); mockSpawnSync.mockReturnValue({ status: 0, signal: null }); mockFs.readdirSync.mockReturnValue([]); mockFs.existsSync.mockReturnValue(false); }); afterEach(() => { jest.restoreAllMocks(); }); describe('runInit validation', () => { it('should return error code 1 for invalid template', () => { const result = (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'invalid-template', 'react', true, false); expect(result).toBe(1); expect(color_logger_1.default.error).toHaveBeenCalledWith(expect.stringContaining('Invalid template')); }); it('should return error code 1 for invalid framework', () => { const result = (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'invalid-framework', true, false); expect(result).toBe(1); expect(color_logger_1.default.error).toHaveBeenCalledWith(expect.stringContaining('Invalid framework')); }); it('should accept valid templates: field and dataset', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); mockFs.readdirSync.mockReturnValue([]); const resultField = (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); // Should proceed past validation (might fail later on spawn, but not validation) expect(color_logger_1.default.error).not.toHaveBeenCalledWith(expect.stringContaining('Invalid template')); }); it('should accept valid frameworks: none and react', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); const resultNone = (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'none', true, false); expect(color_logger_1.default.error).not.toHaveBeenCalledWith(expect.stringContaining('Invalid framework')); }); it('should use default path from process.cwd() if not provided', () => { const cwdSpy = jest.spyOn(process, 'cwd').mockReturnValue('/default/path'); mockSpawnSync.mockReturnValue({ status: 0, signal: null }); mockFs.readdirSync.mockReturnValue([]); (0, init_pcf_1.runInit)('', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); expect(cwdSpy).toHaveBeenCalled(); cwdSpy.mockRestore(); }); it('should pass correct parameters to pac pcf init command', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); mockFs.readdirSync.mockReturnValue([]); (0, init_pcf_1.runInit)('/test/path', 'MyControl', 'MyPublisher', 'mp', 'dataset', 'react', true, false); expect(mockSpawnSync).toHaveBeenCalledWith('pac', expect.arrayContaining([ 'pcf', 'init', '-ns', 'mp', '-n', 'MyControl', '-t', 'dataset', '-fw', 'react', '-o', '/test/path', '-npm', 'true' ]), expect.any(Object)); }); it('should pass npm false when npm install is disabled', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); mockFs.readdirSync.mockReturnValue([]); (0, init_pcf_1.runInit)('/test/path', 'MyControl', 'MyPublisher', 'mp', 'field', 'react', false, false); expect(mockSpawnSync).toHaveBeenCalledWith('pac', expect.arrayContaining(['pcf', 'init', '-npm', 'false']), expect.any(Object)); }); it('should return error if pac pcf init fails', () => { mockSpawnSync.mockReturnValue({ status: 1, signal: null, error: new Error('init failed') }); const result = (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); // Should exit with error from init task expect(result).toBe(1); }); }); describe('pcfExistsInParent logic', () => { it('should detect .pcfproj file at current level', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); // First call (at root) returns .pcfproj file mockFs.readdirSync .mockReturnValueOnce(['.pcfproj']) .mockReturnValueOnce(['.pcfproj', 'Solutions']); mockFs.existsSync.mockReturnValue(true); mockFs.realpathSync.mockReturnValue('/test/path'); (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); // Should find PCF project at root expect(mockFs.readdirSync).toHaveBeenCalled(); }); it('should throw error if PCF project not found within 3 levels', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); // No .pcfproj found at any level mockFs.readdirSync.mockReturnValue(['package.json', 'tsconfig.json']); const result = (0, init_pcf_1.runInit)('/deep/nested/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); // Should return error expect(result).toBe(1); expect(color_logger_1.default.error).toHaveBeenCalledWith(expect.stringContaining('Unable to locate PCF project')); }); it('should create Solutions folder path if PCF exists at root', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); // PCF exists at root mockFs.readdirSync .mockReturnValueOnce(['control.pcfproj']) .mockReturnValueOnce(['control.pcfproj', 'Solutions']); mockFs.existsSync.mockReturnValue(true); mockFs.realpathSync.mockReturnValue('/test/path'); (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); const callArgs = mockSpawnSync.mock.calls.find((call) => call[0] === 'pac' && Array.isArray(call[1]) && call[1][0] === 'solution' && call[1][1] === 'init'); expect(callArgs).toBeDefined(); if (callArgs) { const hasSolutionsPath = callArgs[1].some((arg) => arg.includes('Solutions')); expect(hasSolutionsPath).toBe(true); } }); }); describe('solution initialization', () => { it('should create solution if pcf init succeeds', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); mockFs.readdirSync.mockReturnValue(['control.pcfproj']); mockFs.existsSync.mockReturnValue(true); mockFs.realpathSync.mockReturnValue('/test/path'); (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); const solutionInitCall = mockSpawnSync.mock.calls.find((call) => call[0] === 'pac' && Array.isArray(call[1]) && call[1][0] === 'solution' && call[1][1] === 'init'); expect(solutionInitCall).toBeDefined(); }); it('should add solution reference to pcf project', () => { mockSpawnSync.mockReturnValue({ status: 0, signal: null }); mockFs.readdirSync.mockReturnValue(['control.pcfproj']); mockFs.existsSync.mockReturnValue(true); mockFs.realpathSync.mockReturnValue('/test/path'); (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); const hasAddRefCall = mockSpawnSync.mock.calls.some((call) => call[0] === 'pac' && Array.isArray(call[1]) && call[1][0] === 'solution' && call[1][1] === 'add-reference'); expect(hasAddRefCall).toBe(true); }); it('should return error if solution init fails', () => { mockSpawnSync .mockReturnValueOnce({ status: 0, signal: null }) .mockReturnValueOnce({ status: 1, signal: null, error: new Error('solution init failed') }); mockFs.readdirSync.mockReturnValue(['.pcfproj']); const result = (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); expect(result).toBe(1); }); }); describe('verbose logging', () => { it('should not log error details when verbose is false', () => { mockSpawnSync.mockReturnValue({ status: 1, signal: null, error: new Error('some error') }); mockFs.readdirSync.mockReturnValue([]); (0, init_pcf_1.runInit)('/test/path', 'myControl', 'MyPublisher', 'mp', 'field', 'react', true, false); expect(color_logger_1.default.debug).not.toHaveBeenCalledWith(expect.stringContaining('stack')); }); }); });