From c545e4acfc057a50c927b0982042d80baae2bafc Mon Sep 17 00:00:00 2001 From: Kirtesh Suthar Date: Mon, 26 May 2025 16:58:52 +0530 Subject: [PATCH 1/6] fix: update LightLivePreviewHoC config to use IExportedConfig type --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 36b903f5..f216bd50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import ContentstackLivePreviewHOC from "./preview/contentstack-live-preview-HOC"; -import { IStackSdk as ExternalStackSdkType } from "./types/types"; +import { IStackSdk as ExternalStackSdkType, IExportedConfig } from "./types/types"; import { OnEntryChangeCallback, @@ -37,7 +37,7 @@ class LightLivePreviewHoC { } static get config() { - return {}; + return {} as IExportedConfig; } static isInitialized() { From e248309141411ac1398b7661bdc5bd83dfab8857 Mon Sep 17 00:00:00 2001 From: Kirtesh Suthar Date: Mon, 26 May 2025 18:02:37 +0530 Subject: [PATCH 2/6] refactor: move LightLivePreviewHoC to a new file and simplify index.ts --- .../contentstack-live-preview.test.ts | 177 ++++++++++++++++++ src/index.ts | 68 +------ src/light-sdk.ts | 67 +++++++ 3 files changed, 246 insertions(+), 66 deletions(-) create mode 100644 src/__test__/contentstack-live-preview.test.ts create mode 100644 src/light-sdk.ts diff --git a/src/__test__/contentstack-live-preview.test.ts b/src/__test__/contentstack-live-preview.test.ts new file mode 100644 index 00000000..e229a1f8 --- /dev/null +++ b/src/__test__/contentstack-live-preview.test.ts @@ -0,0 +1,177 @@ +import { vi } from 'vitest'; +import type { OnEntryChangeCallback, OnEntryChangeConfig } from '../livePreview/types/onEntryChangeCallback.type'; +import type { IStackSdk, IExportedConfig, IInitData } from '../types/types'; + +// Store original env and window +const originalEnv = process.env; +const originalWindow = global.window; + +describe('ContentstackLivePreview HOC Class Integration Tests', () => { + let ContentstackLivePreview: any; + let ContentstackLivePreviewHOC: any; + + beforeEach(() => { + // Reset modules and env before each test + vi.resetModules(); + process.env = { ...originalEnv }; + }); + + afterEach(() => { + process.env = originalEnv; + vi.clearAllMocks(); + + // Restore window to original state + Object.defineProperty(global, 'window', { + value: originalWindow, + writable: true, + configurable: true + }); + }); + + describe('SDK Selection Tests', () => { + test('should use LightLivePreviewHoC when PURGE_PREVIEW_SDK is true', async () => { + process.env.PURGE_PREVIEW_SDK = 'true'; + + const { default: CSLivePreview } = await import('../index'); + const { default: CSLivePreviewHOC } = await import('../preview/contentstack-live-preview-HOC'); + + ContentstackLivePreview = CSLivePreview; + ContentstackLivePreviewHOC = CSLivePreviewHOC; + + expect(ContentstackLivePreview.name).toBe('LightLivePreviewHoC'); + }); + + test('should use LightLivePreviewHoC when REACT_APP_PURGE_PREVIEW_SDK is true', async () => { + process.env.REACT_APP_PURGE_PREVIEW_SDK = 'true'; + + const { default: CSLivePreview } = await import('../index'); + ContentstackLivePreview = CSLivePreview; + + expect(ContentstackLivePreview.name).toBe('LightLivePreviewHoC'); + }); + + test('should use ContentstackLivePreviewHOC by default', async () => { + process.env.PURGE_PREVIEW_SDK = 'false'; + process.env.REACT_APP_PURGE_PREVIEW_SDK = 'false'; + + const { default: CSLivePreview } = await import('../index'); + const { default: CSLivePreviewHOC } = await import('../preview/contentstack-live-preview-HOC'); + + ContentstackLivePreview = CSLivePreview; + ContentstackLivePreviewHOC = CSLivePreviewHOC; + + expect(ContentstackLivePreview).toBe(ContentstackLivePreviewHOC); + }); + }); + + describe('LightLivePreviewHoC Functionality Tests', () => { + beforeEach(async () => { + process.env.PURGE_PREVIEW_SDK = 'true'; + const { default: CSLivePreview } = await import('../index'); + ContentstackLivePreview = CSLivePreview; + }); + + test('should initialize with empty constructors', async () => { + const result = await ContentstackLivePreview.init(); + expect(result).toEqual({ + livePreview: {}, + visualBuilder: {}, + }); + }); + + test('should return empty hash', () => { + expect(ContentstackLivePreview.hash).toBe(''); + }); + + test('should return empty config', () => { + const config = ContentstackLivePreview.config as IExportedConfig; + expect(config).toEqual({}); + }); + + test('should allow access to stackDetails from config without type error', () => { + expect(ContentstackLivePreview.config.stackDetails).toEqual(undefined); + }); + + test('should handle onEntryChange with immediate callback execution', () => { + const mockCallback = vi.fn() as OnEntryChangeCallback; + const config: OnEntryChangeConfig = { skipInitialRender: false }; + + const callbackId = ContentstackLivePreview.onEntryChange(mockCallback, config); + + expect(callbackId).toBe('live-preview-id'); + expect(mockCallback).toHaveBeenCalledTimes(1); + }); + + test('should handle onEntryChange with skipped initial render', () => { + const mockCallback = vi.fn() as OnEntryChangeCallback; + const config: OnEntryChangeConfig = { skipInitialRender: true }; + + const callbackId = ContentstackLivePreview.onEntryChange(mockCallback, config); + + expect(callbackId).toBe('live-preview-id'); + expect(mockCallback).not.toHaveBeenCalled(); + }); + + test('should handle onLiveEdit', () => { + const mockCallback = vi.fn() as OnEntryChangeCallback; + const result = ContentstackLivePreview.onLiveEdit(mockCallback); + expect(result).toBe('live-preview-id'); + }); + + test('should handle unsubscribeOnEntryChange without errors', () => { + const mockCallback = vi.fn() as OnEntryChangeCallback; + const callbackId = ContentstackLivePreview.onEntryChange(mockCallback); + expect(() => { + ContentstackLivePreview.unsubscribeOnEntryChange(callbackId); + }).not.toThrow(); + }); + + test('should return package version from environment', () => { + process.env.PACKAGE_VERSION = '1.0.0'; + expect(ContentstackLivePreview.getSdkVersion()).toBe('1.0.0'); + }); + }); + + describe('Browser Environment Tests', () => { + beforeEach(async () => { + process.env.PURGE_PREVIEW_SDK = 'true'; + vi.resetModules(); + const { default: CSLivePreview } = await import('../index'); + ContentstackLivePreview = CSLivePreview; + }); + + test('should handle initialization in non-browser environment', async () => { + // Mock window as undefined + Object.defineProperty(global, 'window', { + value: undefined, + writable: true, + configurable: true + }); + + const result = await ContentstackLivePreview.init(); + + expect(result).toEqual({}); + }); + + test('should initialize properly in browser environment', async () => { + // Mock window with minimum required properties + Object.defineProperty(global, 'window', { + value: { + location: { + search: '', + hash: '' + } + }, + writable: true, + configurable: true + }); + + const result = await ContentstackLivePreview.init(); + + expect(result).toEqual({ + livePreview: {}, + visualBuilder: {}, + }); + }); + }); +}); diff --git a/src/index.ts b/src/index.ts index f216bd50..45074abc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,74 +1,10 @@ import ContentstackLivePreviewHOC from "./preview/contentstack-live-preview-HOC"; -import { IStackSdk as ExternalStackSdkType, IExportedConfig } from "./types/types"; +import { IStackSdk as ExternalStackSdkType } from "./types/types"; -import { - OnEntryChangeCallback, - OnEntryChangeCallbackUID, - OnEntryChangeConfig, -} from "./livePreview/types/onEntryChangeCallback.type"; +import LightLivePreviewHoC from "./light-sdk"; export type IStackSdk = ExternalStackSdkType; -class LightLivePreviewHoC { - private static previewConstructors = {}; - private static onEntryChangeCallbacks = {}; - - static init() { - if (typeof window === "undefined") { - return Promise.resolve(LightLivePreviewHoC.previewConstructors); - } - - return LightLivePreviewHoC.initializePreview(); - } - - private static initializePreview() { - LightLivePreviewHoC.previewConstructors = { - livePreview: {}, - visualBuilder: {}, - }; - - LightLivePreviewHoC.onEntryChangeCallbacks = {}; - - return Promise.resolve(LightLivePreviewHoC.previewConstructors); - } - - static get hash() { - return ""; - } - - static get config() { - return {} as IExportedConfig; - } - - static isInitialized() { - return false; - } - - static onEntryChange( - callback: OnEntryChangeCallback, - config: OnEntryChangeConfig = {} - ): OnEntryChangeCallbackUID { - const { skipInitialRender = false } = config; - - if (!skipInitialRender) { - callback(); - } - - return "live-preview-id"; - } - - static onLiveEdit(callback: OnEntryChangeCallback) { - return "live-preview-id"; - } - - static unsubscribeOnEntryChange() { - // intentionally empty - } - - static getSdkVersion(): string { - return process?.env?.PACKAGE_VERSION!; - } -} const ContentstackLivePreview = typeof process !== "undefined" && diff --git a/src/light-sdk.ts b/src/light-sdk.ts new file mode 100644 index 00000000..becf0ad1 --- /dev/null +++ b/src/light-sdk.ts @@ -0,0 +1,67 @@ +import { OnEntryChangeCallbackUID, OnEntryChangeConfig } from "./livePreview/types/onEntryChangeCallback.type"; + +import { OnEntryChangeCallback } from "./livePreview/types/onEntryChangeCallback.type"; +import { IExportedConfig } from "./types/types"; + +class LightLivePreviewHoC { + private static previewConstructors = {}; + private static onEntryChangeCallbacks = {}; + + static init() { + if (typeof window === "undefined") { + return Promise.resolve(LightLivePreviewHoC.previewConstructors); + } + + return LightLivePreviewHoC.initializePreview(); + } + + private static initializePreview() { + LightLivePreviewHoC.previewConstructors = { + livePreview: {}, + visualBuilder: {}, + }; + + LightLivePreviewHoC.onEntryChangeCallbacks = {}; + + return Promise.resolve(LightLivePreviewHoC.previewConstructors); + } + + static get hash() { + return ""; + } + + static get config() { + return {} as IExportedConfig; + } + + static isInitialized() { + return false; + } + + static onEntryChange( + callback: OnEntryChangeCallback, + config: OnEntryChangeConfig = {} + ): OnEntryChangeCallbackUID { + const { skipInitialRender = false } = config; + + if (!skipInitialRender) { + callback(); + } + + return "live-preview-id"; + } + + static onLiveEdit(callback: OnEntryChangeCallback) { + return "live-preview-id"; + } + + static unsubscribeOnEntryChange() { + // intentionally empty + } + + static getSdkVersion(): string { + return process?.env?.PACKAGE_VERSION!; + } +} + +export default LightLivePreviewHoC; \ No newline at end of file From 1f024ce31c299cd312cfb26ba85498d967f02d25 Mon Sep 17 00:00:00 2001 From: Kirtesh Suthar Date: Mon, 26 May 2025 18:05:09 +0530 Subject: [PATCH 3/6] fix: update script integrity hash in README.md for Contentstack Live Preview utils --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8ca55f6..9eed318c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ npm install @contentstack/live-preview-utils Alternatively, if you want to include the package directly in your website HTML code, use the following command: ```html -