@linagora/linid-im-front-corelib 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,7 +24,6 @@
24
24
  * LinID Identity Manager software.
25
25
  */
26
26
 
27
- import type { App } from 'vue';
28
27
  import type { ModuleHostConfig, RemoteModule } from '../types/module';
29
28
  import type { ModuleLifecycleResult } from '../types/moduleLifecycle';
30
29
 
@@ -81,10 +80,9 @@ export class BasicRemoteModule implements RemoteModule {
81
80
  *
82
81
  * Default implementation returns success.
83
82
  * Override this method to add custom setup logic.
84
- * @param _app - The Vue application instance.
85
83
  * @returns Promise resolving to the lifecycle result.
86
84
  */
87
- async onSetup(_app: App): Promise<ModuleLifecycleResult> {
85
+ async setup(): Promise<ModuleLifecycleResult> {
88
86
  return { success: true };
89
87
  }
90
88
 
@@ -96,13 +94,11 @@ export class BasicRemoteModule implements RemoteModule {
96
94
  *
97
95
  * Default implementation returns success.
98
96
  * Override this method to add custom configuration logic.
99
- * @param _app - The Vue application instance.
100
- * @param _config - Module-specific configuration from host.
97
+ * @param config - Module-specific configuration from host.
101
98
  * @returns Promise resolving to the lifecycle result.
102
99
  */
103
- async onConfigure(
104
- _app: App,
105
- _config: ModuleHostConfig
100
+ async configure(
101
+ config: ModuleHostConfig /* eslint-disable-line @typescript-eslint/no-unused-vars */
106
102
  ): Promise<ModuleLifecycleResult> {
107
103
  return { success: true };
108
104
  }
@@ -115,10 +111,9 @@ export class BasicRemoteModule implements RemoteModule {
115
111
  *
116
112
  * Default implementation returns success.
117
113
  * Override this method to add custom initialization logic.
118
- * @param _app - The Vue application instance.
119
114
  * @returns Promise resolving to the lifecycle result.
120
115
  */
121
- async onInitialize(_app: App): Promise<ModuleLifecycleResult> {
116
+ async initialize(): Promise<ModuleLifecycleResult> {
122
117
  return { success: true };
123
118
  }
124
119
 
@@ -130,10 +125,9 @@ export class BasicRemoteModule implements RemoteModule {
130
125
  *
131
126
  * Default implementation returns success.
132
127
  * Override this method to add custom ready logic.
133
- * @param _app - The Vue application instance.
134
128
  * @returns Promise resolving to the lifecycle result.
135
129
  */
136
- async onReady(_app: App): Promise<ModuleLifecycleResult> {
130
+ async ready(): Promise<ModuleLifecycleResult> {
137
131
  return { success: true };
138
132
  }
139
133
 
@@ -145,10 +139,9 @@ export class BasicRemoteModule implements RemoteModule {
145
139
  *
146
140
  * Default implementation returns success.
147
141
  * Override this method to add custom post-init logic.
148
- * @param _app - The Vue application instance.
149
142
  * @returns Promise resolving to the lifecycle result.
150
143
  */
151
- async onPostInit(_app: App): Promise<ModuleLifecycleResult> {
144
+ async postInit(): Promise<ModuleLifecycleResult> {
152
145
  return { success: true };
153
146
  }
154
147
  }
@@ -24,7 +24,6 @@
24
24
  * LinID Identity Manager software.
25
25
  */
26
26
 
27
- import type { App } from 'vue';
28
27
  import type { ModuleHostConfig } from './module';
29
28
 
30
29
  /**
@@ -67,7 +66,7 @@ export enum ModuleLifecyclePhase {
67
66
  *
68
67
  * Use this phase for cross-module integrations and final setup.
69
68
  */
70
- POST_INIT = 'post-init',
69
+ POST_INIT = 'postInit',
71
70
  }
72
71
 
73
72
  /**
@@ -112,52 +111,44 @@ export interface ModuleLifecycleHooks {
112
111
  *
113
112
  * Use this to prepare the module for initialization and validate
114
113
  * that all required dependencies are available.
115
- * @param app - The Vue application instance.
116
114
  * @returns Promise resolving to the lifecycle result.
117
115
  */
118
- onSetup?(app: App): Promise<ModuleLifecycleResult>;
116
+ setup(): Promise<ModuleLifecycleResult>;
119
117
 
120
118
  /**
121
119
  * Called to configure the module with application-specific settings.
122
120
  *
123
121
  * Use this to receive and validate the host configuration for your module.
124
122
  * This is where you should check that all required configuration is present.
125
- * @param app - The Vue application instance.
126
123
  * @param config - Module-specific configuration from host (from module-<name>.json).
127
124
  * @returns Promise resolving to the lifecycle result.
128
125
  */
129
- onConfigure?(
130
- app: App,
131
- config: ModuleHostConfig
132
- ): Promise<ModuleLifecycleResult>;
126
+ configure(config: ModuleHostConfig): Promise<ModuleLifecycleResult>;
133
127
 
134
128
  /**
135
129
  * Called to initialize the module's core functionality.
136
130
  *
137
131
  * Use this to register Pinia stores and initialize any resources
138
132
  * your module needs to function.
139
- * @param app - The Vue application instance.
140
133
  * @returns Promise resolving to the lifecycle result.
141
134
  */
142
- onInitialize?(app: App): Promise<ModuleLifecycleResult>;
135
+ initialize(): Promise<ModuleLifecycleResult>;
143
136
 
144
137
  /**
145
138
  * Called when the module is ready to be used.
146
139
  *
147
140
  * Use this to perform final checks and emit ready state.
148
141
  * At this point, all other modules have completed initialization.
149
- * @param app - The Vue application instance.
150
142
  * @returns Promise resolving to the lifecycle result.
151
143
  */
152
- onReady?(app: App): Promise<ModuleLifecycleResult>;
144
+ ready(): Promise<ModuleLifecycleResult>;
153
145
 
154
146
  /**
155
147
  * Called after all modules have been initialized.
156
148
  *
157
149
  * Use this for cross-module integrations and final setup that requires
158
150
  * all modules to be ready.
159
- * @param app - The Vue application instance.
160
151
  * @returns Promise resolving to the lifecycle result.
161
152
  */
162
- onPostInit?(app: App): Promise<ModuleLifecycleResult>;
153
+ postInit(): Promise<ModuleLifecycleResult>;
163
154
  }
@@ -0,0 +1,135 @@
1
+ import { flushPromises, shallowMount } from '@vue/test-utils';
2
+ import { createPinia, setActivePinia } from 'pinia';
3
+ import LinidZoneRenderer from 'src/components/LinidZoneRenderer.vue';
4
+ import * as federationService from 'src/services/federationService';
5
+ import { useLinidZoneStore } from 'src/stores/linidZoneStore';
6
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { nextTick, watch } from 'vue';
8
+
9
+ vi.mock('src/services/federationService', () => ({
10
+ loadAsyncComponent: vi.fn(),
11
+ }));
12
+
13
+ describe('Test component: LinidZoneRenderer', () => {
14
+ let pinia;
15
+ let store;
16
+ let wrapper;
17
+
18
+ beforeEach(() => {
19
+ pinia = createPinia();
20
+ setActivePinia(pinia);
21
+ store = useLinidZoneStore();
22
+ wrapper = shallowMount(LinidZoneRenderer, {
23
+ props: { zone: '' },
24
+ global: { plugins: [pinia] },
25
+ });
26
+ vi.clearAllMocks();
27
+ });
28
+
29
+ describe('Test watchEffect', () => {
30
+ it('should initialize isLoadingComplete to false when mounted', async () => {
31
+ const values = [];
32
+ const stopWatch = watch(
33
+ () => wrapper.vm.isLoadingComplete,
34
+ (newVal) => {
35
+ values.push(newVal);
36
+ },
37
+ { flush: 'sync' }
38
+ );
39
+
40
+ await wrapper.setProps({ zone: 'zone-test' });
41
+
42
+ await flushPromises();
43
+ await nextTick();
44
+ stopWatch();
45
+
46
+ expect(values).toEqual([false, true]);
47
+ });
48
+
49
+ it('should set isLoadingComplete to true after initialization', async () => {
50
+ wrapper.setProps({ zone: 'any-zone' });
51
+
52
+ await flushPromises();
53
+ await nextTick();
54
+
55
+ expect(wrapper.vm.isLoadingComplete).toBe(true);
56
+ });
57
+
58
+ it('should not call loadAsyncComponent when the zone is not registered in store', async () => {
59
+ wrapper.setProps({ zone: 'unregistered-zone' });
60
+
61
+ await flushPromises();
62
+ await nextTick();
63
+
64
+ expect(federationService.loadAsyncComponent).not.toHaveBeenCalled();
65
+ expect(wrapper.vm.components).toEqual([]);
66
+ });
67
+
68
+ it('should not call loadAsyncComponent when the zone is registered has no entries', async () => {
69
+ wrapper.vm.linidZoneStore.zones['empty-zone'] = [];
70
+
71
+ wrapper.setProps({ zone: 'empty-zone' });
72
+
73
+ await flushPromises();
74
+ await nextTick();
75
+
76
+ expect(federationService.loadAsyncComponent).not.toHaveBeenCalled();
77
+ expect(wrapper.vm.components).toEqual([]);
78
+ });
79
+
80
+ it('should load components from store when zone is registered and has entries', async () => {
81
+ const MockComponentA = {
82
+ name: 'MockComponent',
83
+ template: '<div>Mock</div>',
84
+ };
85
+ const MockComponentB = {
86
+ name: 'MockComponent',
87
+ template: '<div>Mock</div>',
88
+ };
89
+ vi.mocked(federationService.loadAsyncComponent)
90
+ .mockReturnValueOnce(MockComponentA)
91
+ .mockReturnValueOnce(MockComponentB);
92
+
93
+ store.register('test-zone', {
94
+ plugin: 'test-plugin/MockComponentA',
95
+ props: { title: 'Test' },
96
+ });
97
+
98
+ store.register('test-zone', {
99
+ plugin: 'test-plugin/MockComponentB',
100
+ props: {},
101
+ });
102
+
103
+ wrapper.setProps({ zone: 'test-zone' });
104
+
105
+ await flushPromises();
106
+ await nextTick();
107
+
108
+ expect(federationService.loadAsyncComponent).toHaveBeenCalledTimes(2);
109
+ expect(federationService.loadAsyncComponent).toHaveBeenCalledWith(
110
+ 'test-plugin/MockComponentA'
111
+ );
112
+ expect(federationService.loadAsyncComponent).toHaveBeenCalledWith(
113
+ 'test-plugin/MockComponentB'
114
+ );
115
+
116
+ expect(wrapper.vm.components).toHaveLength(2);
117
+
118
+ expect(wrapper.vm.components[0].plugin).toBe(
119
+ 'test-plugin/MockComponentA'
120
+ );
121
+ expect(wrapper.vm.components[0].props).toEqual({ title: 'Test' });
122
+ expect(JSON.stringify(wrapper.vm.components[0].component)).toEqual(
123
+ JSON.stringify(MockComponentA)
124
+ );
125
+
126
+ expect(wrapper.vm.components[1].plugin).toBe(
127
+ 'test-plugin/MockComponentB'
128
+ );
129
+ expect(wrapper.vm.components[1].props).toEqual({});
130
+ expect(JSON.stringify(wrapper.vm.components[1].component)).toEqual(
131
+ JSON.stringify(MockComponentB)
132
+ );
133
+ });
134
+ });
135
+ });
@@ -0,0 +1,138 @@
1
+ import { BasicRemoteModule } from 'src/lifecycle/skeleton';
2
+ import { beforeEach, describe, expect, it } from 'vitest';
3
+
4
+ describe('Test class: BasicRemoteModule', () => {
5
+ describe('Test constructor', () => {
6
+ it('should create a module with required parameters', () => {
7
+ const module = new BasicRemoteModule(
8
+ 'test-module',
9
+ 'Test Module',
10
+ '1.0.0'
11
+ );
12
+
13
+ expect(module.id).toBe('test-module');
14
+ expect(module.name).toBe('Test Module');
15
+ expect(module.version).toBe('1.0.0');
16
+ expect(module.description).toBeUndefined();
17
+ });
18
+
19
+ it('should create a module with description', () => {
20
+ const module = new BasicRemoteModule(
21
+ 'test-module',
22
+ 'Test Module',
23
+ '1.0.0',
24
+ 'A test module for unit testing'
25
+ );
26
+
27
+ expect(module.id).toBe('test-module');
28
+ expect(module.name).toBe('Test Module');
29
+ expect(module.version).toBe('1.0.0');
30
+ expect(module.description).toBe('A test module for unit testing');
31
+ });
32
+ });
33
+
34
+ describe('Test lifecycle hooks', () => {
35
+ let module;
36
+
37
+ beforeEach(() => {
38
+ module = new BasicRemoteModule(
39
+ 'test-module',
40
+ 'Test Module',
41
+ '1.0.0',
42
+ 'Test description'
43
+ );
44
+ });
45
+
46
+ describe('Test hook: setup', () => {
47
+ it('should return success by default', async () => {
48
+ const result = await module.setup();
49
+
50
+ expect(result).toEqual({ success: true });
51
+ });
52
+ });
53
+
54
+ describe('Test hook: configure', () => {
55
+ it('should return success with empty config', async () => {
56
+ const result = await module.configure({});
57
+
58
+ expect(result).toEqual({ success: true });
59
+ });
60
+
61
+ it('should return success with valid config', async () => {
62
+ const config = {
63
+ id: 'test-module',
64
+ remoteName: 'testModule',
65
+ };
66
+
67
+ const result = await module.configure(config);
68
+
69
+ expect(result).toEqual({ success: true });
70
+ });
71
+ });
72
+
73
+ describe('Test hook: initialize', () => {
74
+ it('should return success by default', async () => {
75
+ const result = await module.initialize();
76
+
77
+ expect(result).toEqual({ success: true });
78
+ });
79
+ });
80
+
81
+ describe('Test hook: ready', () => {
82
+ it('should return success by default', async () => {
83
+ const result = await module.ready();
84
+
85
+ expect(result).toEqual({ success: true });
86
+ });
87
+ });
88
+
89
+ describe('Test hook: postInit', () => {
90
+ it('should return success by default', async () => {
91
+ const result = await module.postInit();
92
+
93
+ expect(result).toEqual({ success: true });
94
+ });
95
+ });
96
+ });
97
+
98
+ describe('Test module extension', () => {
99
+ it('should allow extending the class', () => {
100
+ class CustomModule extends BasicRemoteModule {
101
+ constructor() {
102
+ super('custom-module', 'Custom Module', '1.0.0');
103
+ }
104
+ }
105
+
106
+ const module = new CustomModule();
107
+
108
+ expect(module).toBeInstanceOf(BasicRemoteModule);
109
+ expect(module.id).toBe('custom-module');
110
+ expect(module.name).toBe('Custom Module');
111
+ expect(module.version).toBe('1.0.0');
112
+ });
113
+
114
+ it('should allow overriding lifecycle methods', async () => {
115
+ class OverriddenModule extends BasicRemoteModule {
116
+ constructor() {
117
+ super('override-module', 'Override Module', '1.0.0');
118
+ }
119
+
120
+ async configure(config) {
121
+ if (!config.required) {
122
+ return { success: false, error: 'Missing required config' };
123
+ }
124
+ return { success: true };
125
+ }
126
+ }
127
+
128
+ const module = new OverriddenModule();
129
+
130
+ const failResult = await module.configure({});
131
+ expect(failResult.success).toBe(false);
132
+ expect(failResult.error).toBe('Missing required config');
133
+
134
+ const successResult = await module.configure({ required: true });
135
+ expect(successResult.success).toBe(true);
136
+ });
137
+ });
138
+ });
@@ -0,0 +1,146 @@
1
+ import { loadRemote } from '@module-federation/enhanced/runtime';
2
+ import { loadAsyncComponent } from 'src/services/federationService';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { defineAsyncComponent } from 'vue';
5
+
6
+ vi.mock('@module-federation/enhanced/runtime');
7
+ vi.mock('vue', async () => {
8
+ const actual = await vi.importActual('vue');
9
+ return {
10
+ ...actual,
11
+ defineAsyncComponent: vi.fn((loader) => loader),
12
+ };
13
+ });
14
+
15
+ describe('Test service: federationService', () => {
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ });
19
+
20
+ describe('Test function: loadAsyncComponent', () => {
21
+ it('should load a remote component successfully', async () => {
22
+ const testComponent = { default: 'RemoteTestComponent' };
23
+ const remoteModule = { './TestComponent': testComponent };
24
+ vi.mocked(loadRemote).mockResolvedValue(remoteModule['./TestComponent']);
25
+
26
+ const loader = loadAsyncComponent('test-plugin/TestComponent');
27
+ const result = await loader();
28
+
29
+ expect(loadRemote).toHaveBeenCalledTimes(1);
30
+ expect(loadRemote).toHaveBeenCalledWith('test-plugin/TestComponent');
31
+ expect(defineAsyncComponent).toHaveBeenCalled();
32
+ expect(result).toEqual('RemoteTestComponent');
33
+ });
34
+
35
+ it('should throw an error if remote component export a null default', async () => {
36
+ const testComponent = { default: null };
37
+ const remoteModule = { './TestComponent': testComponent };
38
+ vi.mocked(loadRemote).mockResolvedValue(remoteModule['./TestComponent']);
39
+
40
+ const loader = loadAsyncComponent('invalid-plugin/TestComponent');
41
+
42
+ await expect(loader()).rejects.toThrow(
43
+ 'Failed to load component from invalid-plugin'
44
+ );
45
+ expect(loadRemote).toHaveBeenCalledWith('invalid-plugin/TestComponent');
46
+ });
47
+
48
+ it('should throw an error if remote component export an undefined default', async () => {
49
+ const testComponent = { default: undefined };
50
+ const remoteModule = { './TestComponent': testComponent };
51
+ vi.mocked(loadRemote).mockResolvedValue(remoteModule['./TestComponent']);
52
+
53
+ const loader = loadAsyncComponent('invalid-plugin/TestComponent');
54
+
55
+ await expect(loader()).rejects.toThrow(
56
+ 'Failed to load component from invalid-plugin'
57
+ );
58
+ expect(loadRemote).toHaveBeenCalledWith('invalid-plugin/TestComponent');
59
+ });
60
+
61
+ it('should throw an error if remote component does not have a default export', async () => {
62
+ const testComponent = { toto: vi.fn() };
63
+ const remoteModule = { './TestComponent': testComponent };
64
+ vi.mocked(loadRemote).mockResolvedValue(remoteModule['./TestComponent']);
65
+
66
+ const loader = loadAsyncComponent('invalid-plugin/TestComponent');
67
+
68
+ await expect(loader()).rejects.toThrow(
69
+ 'Failed to load component from invalid-plugin'
70
+ );
71
+ expect(loadRemote).toHaveBeenCalledWith('invalid-plugin/TestComponent');
72
+ });
73
+
74
+ it('should throw an error if module is null', async () => {
75
+ vi.mocked(loadRemote).mockResolvedValue(null);
76
+
77
+ const loader = loadAsyncComponent('null-plugin/TestComponent');
78
+
79
+ await expect(loader()).rejects.toThrow(
80
+ 'Failed to load component from null-plugin'
81
+ );
82
+ });
83
+
84
+ it('should throw an error if module is undefined', async () => {
85
+ vi.mocked(loadRemote).mockResolvedValue(undefined);
86
+
87
+ const loader = loadAsyncComponent('undefined-plugin/TestComponent');
88
+
89
+ await expect(loader()).rejects.toThrow(
90
+ 'Failed to load component from undefined-plugin'
91
+ );
92
+ });
93
+
94
+ it('should throw an error if loadRemote rejects', async () => {
95
+ const error = new Error('Network error');
96
+
97
+ vi.mocked(loadRemote).mockRejectedValue(error);
98
+
99
+ const loader = loadAsyncComponent('failing-plugin/TestComponent');
100
+
101
+ await expect(loader()).rejects.toThrow('Network error');
102
+ expect(loadRemote).toHaveBeenCalledWith('failing-plugin/TestComponent');
103
+ });
104
+
105
+ it('should handle different plugin names', async () => {
106
+ const plugins = ['plugin-a', 'plugin-b', 'plugin-c'];
107
+ const testComponentA = { default: 'RemoteTestComponentA' };
108
+ const remoteModuleA = { './TestComponent': testComponentA };
109
+ const testComponentB = { default: 'RemoteTestComponentB' };
110
+ const remoteModuleB = { './TestComponent': testComponentB };
111
+ const testComponentC = { default: 'RemoteTestComponentC' };
112
+ const remoteModuleC = { './TestComponent': testComponentC };
113
+
114
+ vi.mocked(loadRemote)
115
+ .mockResolvedValueOnce(remoteModuleA['./TestComponent'])
116
+ .mockResolvedValueOnce(remoteModuleB['./TestComponent'])
117
+ .mockResolvedValueOnce(remoteModuleC['./TestComponent']);
118
+
119
+ for (const plugin of plugins) {
120
+ const loader = loadAsyncComponent(`${plugin}/TestComponent`);
121
+ const result = await loader();
122
+ const lastChar = plugin.charAt(plugin.length - 1).toUpperCase();
123
+
124
+ expect(loadRemote).toHaveBeenCalledWith(`${plugin}/TestComponent`);
125
+ expect(result).toEqual(`RemoteTestComponent${lastChar}`);
126
+ }
127
+
128
+ expect(loadRemote).toHaveBeenCalledTimes(3);
129
+ });
130
+
131
+ it('should handle module with additional exports', async () => {
132
+ const testComponent = {
133
+ default: 'RemoteTestComponent',
134
+ namedExport1: 'value1',
135
+ namedExport2: 'value2',
136
+ };
137
+ const remoteModule = { './TestComponent': testComponent };
138
+ vi.mocked(loadRemote).mockResolvedValue(remoteModule['./TestComponent']);
139
+
140
+ const loader = loadAsyncComponent('multi-export-plugin');
141
+ const result = await loader();
142
+
143
+ expect(result).toEqual('RemoteTestComponent');
144
+ });
145
+ });
146
+ });
@@ -0,0 +1,94 @@
1
+ import { createPinia, setActivePinia } from 'pinia';
2
+ import { useLinidZoneStore } from 'src/stores/linidZoneStore';
3
+ import { beforeEach, describe, expect, it } from 'vitest';
4
+
5
+ describe('Test store: linidZoneStore', () => {
6
+ beforeEach(() => {
7
+ setActivePinia(createPinia());
8
+ });
9
+
10
+ describe('Test initial state', () => {
11
+ it('should initialize with empty zones', () => {
12
+ const store = useLinidZoneStore();
13
+
14
+ expect(store.zones).toEqual({});
15
+ });
16
+ });
17
+
18
+ describe('Test function: register', () => {
19
+ it('should register an entry in a new zone', () => {
20
+ const store = useLinidZoneStore();
21
+ const entry = {
22
+ plugin: 'test-plugin/TestComponent',
23
+ props: {},
24
+ };
25
+
26
+ store.register('list-page.sidebar', entry);
27
+
28
+ expect(store.zones['list-page.sidebar']).toBeDefined();
29
+ expect(store.zones['list-page.sidebar']).toHaveLength(1);
30
+ expect(store.zones['list-page.sidebar'][0]).toEqual(entry);
31
+ });
32
+
33
+ it('should register multiple entries in the same zone', () => {
34
+ const store = useLinidZoneStore();
35
+ const entry1 = {
36
+ plugin: 'plugin-1/Component1',
37
+ props: {},
38
+ };
39
+ const entry2 = {
40
+ plugin: 'plugin-2/Component2',
41
+ props: { value: 42 },
42
+ };
43
+
44
+ store.register('list-page.sidebar', entry1);
45
+ store.register('list-page.sidebar', entry2);
46
+
47
+ expect(store.zones['list-page.sidebar']).toHaveLength(2);
48
+ expect(store.zones['list-page.sidebar'][0]).toEqual(entry1);
49
+ expect(store.zones['list-page.sidebar'][1]).toEqual(entry2);
50
+ });
51
+
52
+ it('should register entries in different zones independently', () => {
53
+ const store = useLinidZoneStore();
54
+ const headerEntry = {
55
+ plugin: 'header-plugin/HeaderComponent',
56
+ props: {},
57
+ };
58
+ const footerEntry = {
59
+ plugin: 'footer-plugin/FooterComponent',
60
+ props: {},
61
+ };
62
+
63
+ store.register('list-page.header', headerEntry);
64
+ store.register('list-page.footer', footerEntry);
65
+
66
+ expect(store.zones['list-page.header']).toHaveLength(1);
67
+ expect(store.zones['list-page.footer']).toHaveLength(1);
68
+ expect(store.zones['list-page.header'][0]).toEqual(headerEntry);
69
+ expect(store.zones['list-page.footer'][0]).toEqual(footerEntry);
70
+ });
71
+
72
+ it('should handle entries with complex props', () => {
73
+ const store = useLinidZoneStore();
74
+ const entry = {
75
+ plugin: 'complex-plugin/ComplexComponent',
76
+ props: {
77
+ title: 'Test Title',
78
+ count: 123,
79
+ enabled: true,
80
+ config: {
81
+ nested: {
82
+ value: 'deep',
83
+ },
84
+ },
85
+ items: ['a', 'b', 'c'],
86
+ },
87
+ };
88
+
89
+ store.register('list-page.body', entry);
90
+
91
+ expect(store.zones['list-page.body'][0].props).toEqual(entry.props);
92
+ });
93
+ });
94
+ });