@linagora/linid-im-front-corelib 0.0.4 → 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.
@@ -120,3 +120,28 @@ jobs:
120
120
  - uses: gensecaihq/Shai-Hulud-2.0-Detector@v1
121
121
  with:
122
122
  fail-on-critical: true
123
+
124
+ unit-tests:
125
+ name: Unit tests
126
+ runs-on: ubuntu-latest
127
+ needs: [build]
128
+ steps:
129
+ - uses: actions/checkout@v4
130
+
131
+ - name: Setup Node.js and pnpm
132
+ uses: ./.github/actions/setup-node-pnpm
133
+ with:
134
+ node-version: ${{ env.NODE_VERSION }}
135
+ pnpm-version: ${{ env.PNPM_VERSION }}
136
+
137
+ - name: Run unit tests
138
+ run: |
139
+ echo '```bash' > coverage.txt
140
+ pnpm test:ci | sed 's/\x1b\[[0-9;]*m//g' >> coverage.txt
141
+ echo '```' >> coverage.txt
142
+
143
+ - name: Comment PR
144
+ uses: JoseThen/comment-pr@v1.2.0
145
+ with:
146
+ file_path: ./coverage.txt
147
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.0.5](https://github.com/linagora/linid-im-front-corelib/compare/v0.0.4...v0.0.5) (2025-12-03)
6
+
5
7
  ### [0.0.4](https://github.com/linagora/linid-im-front-corelib/compare/v0.0.3...v0.0.4) (2025-12-01)
6
8
 
7
9
 
package/CONTRIBUTING.md CHANGED
@@ -161,9 +161,14 @@ npm run dev
161
161
  ## **🧪 Run Tests**
162
162
 
163
163
  ```sh
164
- pnpm test
164
+ pnpm test # Runs the full test suite once
165
+ pnpm test:watch # Runs tests in watch mode
166
+ pnpm test:coverage # Generates a coverage report
167
+
165
168
  # or (not recommended by the dev team)
166
- npm run test
169
+ npm run test # Runs the full test suite once
170
+ npm run test:watch # Runs tests in watch mode
171
+ npm run test:coverage # Generates a coverage report
167
172
  ```
168
173
 
169
174
  ---
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linagora/linid-im-front-corelib",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Core library of the LinID Identity Manager project. Provides shared types, services, components, and utilities for front-end and plugin, enabling consistent integration across the LinID ecosystem.",
5
5
  "type": "module",
6
6
  "main": "dist/core-lib.umd.js",
@@ -13,8 +13,11 @@
13
13
  }
14
14
  },
15
15
  "scripts": {
16
- "build": "vite build && vue-tsc --declaration --emitDeclarationOnly",
17
- "test": "vitest",
16
+ "build": "vite build && vue-tsc --declaration --emitDeclarationOnly -p tsconfig.lib.json",
17
+ "test": "vitest run",
18
+ "test:coverage": "vitest --run --coverage",
19
+ "test:watch": "vitest",
20
+ "test:ci": "vitest run --coverage --reporter=dot",
18
21
  "dev": "vite",
19
22
  "lint": "eslint . --max-warnings 0",
20
23
  "lint:fix": "eslint . --fix",
@@ -58,15 +61,19 @@
58
61
  "@eslint/js": "9.39.1",
59
62
  "@types/node": "20.19.9",
60
63
  "@vitejs/plugin-vue": "6.0.1",
64
+ "@vitest/coverage-v8": "4.0.8",
61
65
  "@vue/eslint-config-prettier": "10.2.0",
62
66
  "@vue/eslint-config-typescript": "14.6.0",
67
+ "@vue/test-utils": "2.4.6",
63
68
  "eslint": "9.39.1",
64
69
  "eslint-plugin-headers": "1.3.3",
65
70
  "eslint-plugin-jsdoc": "61.2.1",
66
71
  "eslint-plugin-vue": "10.5.1",
72
+ "happy-dom": "20.0.10",
67
73
  "prettier": "3.6.2",
68
74
  "typescript": "5.9.3",
69
75
  "vite": "7.2.2",
76
+ "vite-tsconfig-paths": "5.1.4",
70
77
  "vitest": "4.0.8",
71
78
  "vue-tsc": "3.1.3"
72
79
  },
package/eslint.config.js CHANGED
@@ -123,9 +123,10 @@ export default defineConfigWithVueTs(
123
123
  files: [
124
124
  '**/*.test.ts',
125
125
  '**/*.spec.ts',
126
+ '**/*.test.js',
127
+ '**/*.spec.js',
126
128
  '**/__tests__/**',
127
129
  '**/*.config.*',
128
- '**/*.config.*',
129
130
  ],
130
131
  rules: {
131
132
  'jsdoc/require-jsdoc': 'off',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linagora/linid-im-front-corelib",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Core library of the LinID Identity Manager project. Provides shared types, services, components, and utilities for front-end and plugin, enabling consistent integration across the LinID ecosystem.",
5
5
  "type": "module",
6
6
  "main": "dist/core-lib.umd.js",
@@ -47,21 +47,28 @@
47
47
  "@eslint/js": "9.39.1",
48
48
  "@types/node": "20.19.9",
49
49
  "@vitejs/plugin-vue": "6.0.1",
50
+ "@vitest/coverage-v8": "4.0.8",
50
51
  "@vue/eslint-config-prettier": "10.2.0",
51
52
  "@vue/eslint-config-typescript": "14.6.0",
53
+ "@vue/test-utils": "2.4.6",
52
54
  "eslint": "9.39.1",
53
55
  "eslint-plugin-headers": "1.3.3",
54
56
  "eslint-plugin-jsdoc": "61.2.1",
55
57
  "eslint-plugin-vue": "10.5.1",
58
+ "happy-dom": "20.0.10",
56
59
  "prettier": "3.6.2",
57
60
  "typescript": "5.9.3",
58
61
  "vite": "7.2.2",
62
+ "vite-tsconfig-paths": "5.1.4",
59
63
  "vitest": "4.0.8",
60
64
  "vue-tsc": "3.1.3"
61
65
  },
62
66
  "scripts": {
63
- "build": "vite build && vue-tsc --declaration --emitDeclarationOnly",
64
- "test": "vitest",
67
+ "build": "vite build && vue-tsc --declaration --emitDeclarationOnly -p tsconfig.lib.json",
68
+ "test": "vitest run",
69
+ "test:coverage": "vitest --run --coverage",
70
+ "test:watch": "vitest",
71
+ "test:ci": "vitest run --coverage --reporter=dot",
65
72
  "dev": "vite",
66
73
  "lint": "eslint . --max-warnings 0",
67
74
  "lint:fix": "eslint . --fix",
@@ -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
+ });
package/tsconfig.json CHANGED
@@ -1,30 +1,14 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "lib": [
7
- "ESNext",
8
- "DOM"
9
- ],
10
- "allowJs": true,
11
- "esModuleInterop": true,
12
- "strict": true,
13
- "skipLibCheck": true,
14
- "forceConsistentCasingInFileNames": true,
15
- "resolveJsonModule": true,
16
- "types": [
17
- "node",
18
- "vite/client",
19
- "vue"
20
- ],
21
- "declaration": true,
22
- "declarationDir": "dist/types",
23
- "outDir": "dist"
24
- },
25
- "include": [
26
- "src/**/*.ts",
27
- "src/**/*.vue",
28
- "vite.config.ts"
2
+ "files": [],
3
+ "references": [
4
+ {
5
+ "path": "./tsconfig.node.json"
6
+ },
7
+ {
8
+ "path": "./tsconfig.lib.json"
9
+ },
10
+ {
11
+ "path": "./tsconfig.spec.json"
12
+ }
29
13
  ]
30
14
  }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "target": "ESNext",
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "lib": ["ESNext", "DOM"],
8
+ "esModuleInterop": true,
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "types": ["node", "vite/client", "vue"],
14
+ "declaration": true,
15
+ "declarationDir": "dist/types",
16
+ "outDir": "dist"
17
+ },
18
+ "include": ["src"],
19
+ "exclude": ["vite.config.ts", "tests", "vitest.config.ts"]
20
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "module": "nodenext",
5
+ "strict": true,
6
+ "types": ["node"]
7
+ },
8
+ "include": ["vite.config.ts", "vitest.config.ts"]
9
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "baseUrl": ".",
5
+ "outDir": "./out-tsc/vitest",
6
+ "allowJs": true,
7
+ "module": "esnext",
8
+ "moduleResolution": "bundler"
9
+ },
10
+ "include": ["tests", "src"],
11
+ "references": [
12
+ {
13
+ "path": "./tsconfig.lib.json"
14
+ }
15
+ ]
16
+ }
package/vite.config.ts CHANGED
@@ -1,22 +1,17 @@
1
- /// <reference types="node" />
2
- import {defineConfig} from "vite";
3
- import vue from "@vitejs/plugin-vue";
4
- import path from "path";
5
- import fs from "fs";
6
- import {fileURLToPath} from "url";
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
1
+ import vue from '@vitejs/plugin-vue';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { defineConfig } from 'vite';
10
5
 
11
6
  export default defineConfig({
12
7
  plugins: [
13
8
  vue(),
14
9
 
15
10
  {
16
- name: "copy-package-json",
11
+ name: 'copy-package-json',
17
12
  closeBundle() {
18
- const src = path.resolve(__dirname, "package.json");
19
- const dest = path.resolve(__dirname, "dist", "package.json");
13
+ const src = path.resolve(__dirname, 'package.json');
14
+ const dest = path.resolve(__dirname, 'dist', 'package.json');
20
15
  if (fs.existsSync(src)) {
21
16
  fs.copyFileSync(src, dest);
22
17
  }
@@ -26,15 +21,15 @@ export default defineConfig({
26
21
 
27
22
  build: {
28
23
  lib: {
29
- entry: path.resolve(__dirname, "src/index.ts"),
30
- name: "CoreLib",
24
+ entry: path.resolve(__dirname, 'src/index.ts'),
25
+ name: 'CoreLib',
31
26
  fileName: (format) => `core-lib.${format}.js`,
32
27
  },
33
28
  rollupOptions: {
34
- external: ["vue"],
29
+ external: ['vue'],
35
30
  output: {
36
31
  globals: {
37
- vue: "Vue",
32
+ vue: 'Vue',
38
33
  },
39
34
  },
40
35
  },
@@ -0,0 +1,19 @@
1
+ import vue from '@vitejs/plugin-vue';
2
+ import tsconfigPaths from 'vite-tsconfig-paths';
3
+ import { defineConfig } from 'vitest/config';
4
+
5
+ export default defineConfig({
6
+ test: {
7
+ environment: 'happy-dom',
8
+ globals: true,
9
+ include: ['tests/unit/**/*.{test,spec}.js'],
10
+ coverage: {
11
+ provider: 'v8',
12
+ reporter: ['text', 'html', 'lcov'],
13
+ reportsDirectory: './coverage',
14
+ include: ['src/**/*.{ts,js,vue}'],
15
+ exclude: ['**/tests/**', 'src/types/**', 'src/index.ts'],
16
+ },
17
+ },
18
+ plugins: [vue(), tsconfigPaths()],
19
+ });
@@ -1,2 +0,0 @@
1
- declare const _default: import("vite").UserConfig;
2
- export default _default;