mage-obsidian 1.0.12 → 1.1.1
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.
- package/jest.config.js +1 -1
- package/package.json +3 -2
- package/src/__tests__/fixtures/interceptors/emptyTarget.js +2 -0
- package/src/__tests__/fixtures/interceptors/pluginForNonFunction.js +7 -0
- package/src/__tests__/fixtures/interceptors/pluginModule.js +13 -0
- package/src/__tests__/fixtures/interceptors/targetModule.js +12 -0
- package/src/__tests__/fixtures/interceptors/targetNonFunction.js +7 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameA/web/module.config.js +0 -6
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameB/web/module.config.js +0 -5
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameNoConfig/web/module.config.js +0 -6
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/web/theme.config.js +0 -12
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-b/web/theme.config.js +0 -17
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/Vendor_ModuleNameB/web/module.config.js +0 -7
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/web/theme.config.js +0 -14
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-d/web/theme.config.js +0 -11
- package/src/__tests__/service/generateInterceptors.test.js +196 -0
- package/src/__tests__/service/interceptorManager_magento_behavior.test.js +137 -0
- package/src/__tests__/service/themeResolverSync.test.js +4 -75
- package/src/__tests__/vite-plugins/interceptorsPlugin.test.js +121 -0
- package/src/config/default.js +1 -0
- package/src/service/configResolver.js +25 -3
- package/src/service/generateInterceptors.js +268 -0
- package/src/service/interceptorManager.js +137 -0
- package/src/service/interceptorsPlugin.js +70 -0
- package/src/service/moduleResolver.js +6 -18
- package/src/service/themeResolverSync.js +2 -20
- package/src/__tests__/service/moduleResolverSync.test.js +0 -188
- package/src/service/tailwindResolver.js +0 -11
package/jest.config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
testEnvironment: 'node',
|
|
3
3
|
transform: {},
|
|
4
|
-
testPathIgnorePatterns: ['/__tests__/magento_scenarios/','/node_modules/', '/__tests__/__mocks__/'],
|
|
4
|
+
testPathIgnorePatterns: ['/__tests__/magento_scenarios/','/node_modules/', '/__tests__/__mocks__/', '/__tests__/fixtures/'],
|
|
5
5
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mage-obsidian",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Mage Obsidian Ultis",
|
|
5
5
|
"main": "./src/scripts/buildThemes.js",
|
|
6
6
|
"keywords": [
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
".": "./src/scripts/buildThemes.js",
|
|
23
23
|
"./service/": "./src/service/",
|
|
24
24
|
"./utils/": "./src/utils/",
|
|
25
|
-
"./config/": "./src/config/"
|
|
25
|
+
"./config/": "./src/config/",
|
|
26
|
+
"./service/*": "./src/service/*"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"chalk": "^5.4.1",
|
package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/web/theme.config.js
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
tailwind: {
|
|
3
|
-
theme: {
|
|
4
|
-
extend: {},
|
|
5
|
-
},
|
|
6
|
-
plugins: [],
|
|
7
|
-
content: [
|
|
8
|
-
'./**/*.{vue,js}',
|
|
9
|
-
'../**/*.phtml',
|
|
10
|
-
'../*/page_layout/override/base/*.xml',
|
|
11
|
-
],
|
|
12
|
-
},
|
|
13
|
-
ignoredTailwindConfigFromModules: [],
|
|
14
2
|
ignoredCssFromModules: [
|
|
15
3
|
'Vendor_ModuleNameA',
|
|
16
4
|
],
|
package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-b/web/theme.config.js
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
tailwind: {
|
|
3
|
-
theme: {
|
|
4
|
-
extend: {
|
|
5
|
-
textColor: {
|
|
6
|
-
'primary': '#3490dc',
|
|
7
|
-
'secondary': '#ffed4a',
|
|
8
|
-
'danger': '#e3342f',
|
|
9
|
-
},
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
plugins: [],
|
|
13
|
-
content: [
|
|
14
|
-
'./**/*.{vue,js}',
|
|
15
|
-
'../**/*.phtml',
|
|
16
|
-
'../*/page_layout/override/base/*.xml',
|
|
17
|
-
],
|
|
18
|
-
},
|
|
19
2
|
ignoredCssFromModules: [
|
|
20
3
|
'Vendor_ModuleNameNoConfig'
|
|
21
4
|
]
|
package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/web/theme.config.js
CHANGED
|
@@ -1,16 +1,2 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
tailwind: {
|
|
3
|
-
theme: {
|
|
4
|
-
extend: {
|
|
5
|
-
},
|
|
6
|
-
},
|
|
7
|
-
plugins: [],
|
|
8
|
-
content: [
|
|
9
|
-
'./**/*.{vue,js}',
|
|
10
|
-
'../**/*.phtml',
|
|
11
|
-
'../*/page_layout/override/base/*.xml',
|
|
12
|
-
],
|
|
13
|
-
},
|
|
14
|
-
includeTailwindConfigFromParentThemes: false,
|
|
15
|
-
ignoredTailwindConfigFromModules: 'all'
|
|
16
2
|
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
|
|
2
|
+
import { jest } from '@jest/globals';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const fixturesDir = path.resolve(__dirname, '../fixtures/interceptors');
|
|
8
|
+
const targetModulePath = path.join(fixturesDir, 'targetModule.js');
|
|
9
|
+
const pluginModulePath = path.join(fixturesDir, 'pluginModule.js');
|
|
10
|
+
const targetNonFunctionPath = path.join(fixturesDir, 'targetNonFunction.js');
|
|
11
|
+
const pluginForNonFunctionPath = path.join(fixturesDir, 'pluginForNonFunction.js');
|
|
12
|
+
|
|
13
|
+
describe('generateInterceptors', () => {
|
|
14
|
+
let generateInterceptorsService;
|
|
15
|
+
let themeResolverMock;
|
|
16
|
+
let moduleResolverMock;
|
|
17
|
+
let interceptorManager;
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
jest.resetModules();
|
|
21
|
+
|
|
22
|
+
themeResolverMock = {
|
|
23
|
+
getThemeConfig: jest.fn(),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
moduleResolverMock = {
|
|
27
|
+
getModuleConfigByThemeConfig: jest.fn(),
|
|
28
|
+
getAllJsVueFilesWithInheritanceCached: jest.fn(),
|
|
29
|
+
getAllJsVueFilesWithInheritance: jest.fn(),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
jest.unstable_mockModule('../../service/themeResolverSync.js', () => ({
|
|
33
|
+
default: themeResolverMock
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
jest.unstable_mockModule('../../service/moduleResolver.js', () => ({
|
|
37
|
+
default: moduleResolverMock
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
jest.unstable_mockModule('../../service/configResolver.js', () => ({
|
|
41
|
+
default: {
|
|
42
|
+
resolveLibRealPath: jest.fn().mockReturnValue('/mocked/path/to/interceptorManager')
|
|
43
|
+
}
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// Import real interceptorManager to reset state
|
|
47
|
+
interceptorManager = (await import('../../service/interceptorManager.js')).default;
|
|
48
|
+
interceptorManager.interceptors = {};
|
|
49
|
+
|
|
50
|
+
generateInterceptorsService = (await import('../../service/generateInterceptors.js')).default;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should generate interceptors correctly', async () => {
|
|
54
|
+
const themeName = 'Vendor/theme-test';
|
|
55
|
+
|
|
56
|
+
themeResolverMock.getThemeConfig.mockReturnValue({
|
|
57
|
+
src: '/path/to/theme'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
moduleResolverMock.getModuleConfigByThemeConfig.mockResolvedValue({
|
|
61
|
+
interceptors: {
|
|
62
|
+
'TestPlugin': {
|
|
63
|
+
name: 'TestPlugin',
|
|
64
|
+
target: 'Vendor_TargetModule::js/target.js',
|
|
65
|
+
interceptor: 'Vendor_PluginModule::js/plugin.js',
|
|
66
|
+
sortOrder: 10,
|
|
67
|
+
active: true
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const allFilesMap = {
|
|
73
|
+
'Vendor_TargetModule/js/target': targetModulePath,
|
|
74
|
+
'Vendor_PluginModule/js/plugin': pluginModulePath
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
moduleResolverMock.getAllJsVueFilesWithInheritanceCached.mockReturnValue(allFilesMap);
|
|
78
|
+
|
|
79
|
+
const result = await generateInterceptorsService.generateInterceptors(themeName);
|
|
80
|
+
|
|
81
|
+
const targetIdentifier = 'Vendor_TargetModule::js/target.js';
|
|
82
|
+
// Use bracket access check to avoid Jest treating '.' as nested property
|
|
83
|
+
expect(result[targetIdentifier]).toBeDefined();
|
|
84
|
+
|
|
85
|
+
const interceptorData = result[targetIdentifier];
|
|
86
|
+
expect(interceptorData).toHaveProperty('proxy');
|
|
87
|
+
expect(interceptorData).toHaveProperty('source');
|
|
88
|
+
expect(interceptorData).toHaveProperty('targetPath', targetModulePath);
|
|
89
|
+
expect(interceptorData.interceptors).toHaveLength(1);
|
|
90
|
+
expect(interceptorData.interceptors[0].name).toBe('TestPlugin');
|
|
91
|
+
|
|
92
|
+
// Verify Source Code Generation
|
|
93
|
+
const source = interceptorData.source;
|
|
94
|
+
expect(source).toContain(`import * as originalModule from '/@fs${targetModulePath}?originalIntercepted';`);
|
|
95
|
+
expect(source).toContain(`import * as interceptor_0 from '/@fs${pluginModulePath}';`);
|
|
96
|
+
expect(source).toContain(`interceptorManager.addInterceptor('${targetIdentifier}::targetFunction', 'TestPlugin', 'before', interceptor_0.beforeTargetFunction, 10);`);
|
|
97
|
+
expect(source).toContain(`interceptorManager.addInterceptor('${targetIdentifier}::default', 'TestPlugin', 'before', interceptor_0.beforeDefault, 10);`);
|
|
98
|
+
expect(source).toContain(`export const targetFunction = proxy.targetFunction;`);
|
|
99
|
+
|
|
100
|
+
// Verify Proxy Behavior
|
|
101
|
+
const proxy = interceptorData.proxy;
|
|
102
|
+
const resultFunc = await proxy.targetFunction('test');
|
|
103
|
+
expect(resultFunc).toBe('Original: Modified: test');
|
|
104
|
+
|
|
105
|
+
const resultAnother = await proxy.anotherFunction();
|
|
106
|
+
expect(resultAnother).toBe('Another - Modified');
|
|
107
|
+
|
|
108
|
+
const resultDefault = await proxy.default();
|
|
109
|
+
expect(resultDefault).toBe('Default'); // The mock plugin returns ['Default Modified'] but the original function ignores args and returns 'Default'.
|
|
110
|
+
// Wait, before interceptor modifies arguments.
|
|
111
|
+
// targetModule.js: export default function defaultExport() { return 'Default'; }
|
|
112
|
+
// It doesn't take arguments, so modifying arguments won't change output unless we check arguments.
|
|
113
|
+
// But we just want to verify it was registered.
|
|
114
|
+
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('should handle missing target module gracefully', async () => {
|
|
118
|
+
themeResolverMock.getThemeConfig.mockReturnValue({});
|
|
119
|
+
moduleResolverMock.getModuleConfigByThemeConfig.mockResolvedValue({
|
|
120
|
+
'Vendor_TargetModule': { interceptors: [] }
|
|
121
|
+
});
|
|
122
|
+
moduleResolverMock.getAllJsVueFilesWithInheritanceCached.mockReturnValue({});
|
|
123
|
+
|
|
124
|
+
const result = await generateInterceptorsService.generateInterceptors('Vendor/theme-missing');
|
|
125
|
+
expect(result).toEqual({});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('should throw error if plugin targets non-existent method', async () => {
|
|
129
|
+
const themeName = 'Vendor/theme-test';
|
|
130
|
+
themeResolverMock.getThemeConfig.mockReturnValue({});
|
|
131
|
+
moduleResolverMock.getModuleConfigByThemeConfig.mockResolvedValue({
|
|
132
|
+
interceptors: {
|
|
133
|
+
'BadPlugin': {
|
|
134
|
+
name: 'BadPlugin',
|
|
135
|
+
target: 'Vendor_TargetModule::js/target.js',
|
|
136
|
+
interceptor: 'Vendor_PluginModule::js/plugin.js',
|
|
137
|
+
sortOrder: 10
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const emptyTargetModulePath = path.join(fixturesDir, 'emptyTarget.js');
|
|
143
|
+
|
|
144
|
+
const allFilesMap = {
|
|
145
|
+
'Vendor_TargetModule/js/target': emptyTargetModulePath,
|
|
146
|
+
'Vendor_PluginModule/js/plugin': pluginModulePath
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
moduleResolverMock.getAllJsVueFilesWithInheritanceCached.mockReturnValue(allFilesMap);
|
|
150
|
+
|
|
151
|
+
// We expect the promise to reject because generateInterceptors throws on invalid target method
|
|
152
|
+
await expect(generateInterceptorsService.generateInterceptors(themeName))
|
|
153
|
+
.rejects
|
|
154
|
+
.toThrow(/does not export/);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('should skip interception for non-function exports', async () => {
|
|
158
|
+
const themeName = 'Vendor/theme-test';
|
|
159
|
+
|
|
160
|
+
themeResolverMock.getThemeConfig.mockReturnValue({
|
|
161
|
+
src: '/path/to/theme'
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
moduleResolverMock.getModuleConfigByThemeConfig.mockResolvedValue({
|
|
165
|
+
interceptors: {
|
|
166
|
+
'TestPlugin': {
|
|
167
|
+
name: 'TestPlugin',
|
|
168
|
+
target: 'Vendor_TargetModule::js/targetNonFunction.js',
|
|
169
|
+
interceptor: 'Vendor_PluginModule::js/pluginForNonFunction.js',
|
|
170
|
+
sortOrder: 10,
|
|
171
|
+
active: true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const allFilesMap = {
|
|
177
|
+
'Vendor_TargetModule/js/targetNonFunction': targetNonFunctionPath,
|
|
178
|
+
'Vendor_PluginModule/js/pluginForNonFunction': pluginForNonFunctionPath
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
moduleResolverMock.getAllJsVueFilesWithInheritanceCached.mockReturnValue(allFilesMap);
|
|
182
|
+
|
|
183
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
184
|
+
|
|
185
|
+
const result = await generateInterceptorsService.generateInterceptors(themeName);
|
|
186
|
+
|
|
187
|
+
const targetIdentifier = 'Vendor_TargetModule::js/targetNonFunction.js';
|
|
188
|
+
|
|
189
|
+
// Since both 'config' and 'default' are objects, and we are trying to intercept them,
|
|
190
|
+
// they should be skipped. If all are skipped, the targetIdentifier should not be in result.
|
|
191
|
+
expect(result[targetIdentifier]).toBeUndefined();
|
|
192
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('is not a function'));
|
|
193
|
+
|
|
194
|
+
consoleSpy.mockRestore();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
|
|
2
|
+
import { jest } from '@jest/globals';
|
|
3
|
+
import interceptorManager from '../../service/interceptorManager.js';
|
|
4
|
+
|
|
5
|
+
describe('InterceptorManager Magento Behavior', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
// Reset interceptors before each test
|
|
8
|
+
interceptorManager.interceptors = {};
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('Before interceptors: should chain arguments and handle null returns', async () => {
|
|
12
|
+
const targetName = 'Test::beforeChain';
|
|
13
|
+
const originalFn = jest.fn((arg1, arg2) => `Original: ${arg1}, ${arg2}`);
|
|
14
|
+
const context = {};
|
|
15
|
+
|
|
16
|
+
// Interceptor 1: Modifies arguments
|
|
17
|
+
const beforeInterceptor1 = jest.fn((arg1, arg2) => {
|
|
18
|
+
return [arg1 + '_mod1', arg2 + '_mod1'];
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Interceptor 2: Returns null (should keep arguments from Interceptor 1)
|
|
22
|
+
const beforeInterceptor2 = jest.fn((arg1, arg2) => {
|
|
23
|
+
return null;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Interceptor 3: Modifies arguments again
|
|
27
|
+
const beforeInterceptor3 = jest.fn((arg1, arg2) => {
|
|
28
|
+
return [arg1 + '_mod3', arg2 + '_mod3'];
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
interceptorManager.addInterceptor(targetName, 'Interceptor1', 'before', beforeInterceptor1, 10);
|
|
32
|
+
interceptorManager.addInterceptor(targetName, 'Interceptor2', 'before', beforeInterceptor2, 20);
|
|
33
|
+
interceptorManager.addInterceptor(targetName, 'Interceptor3', 'before', beforeInterceptor3, 30);
|
|
34
|
+
|
|
35
|
+
const result = await interceptorManager.execute(targetName, originalFn, context, 'A', 'B');
|
|
36
|
+
|
|
37
|
+
// Verify Interceptor 1 received original args
|
|
38
|
+
expect(beforeInterceptor1).toHaveBeenCalledWith('A', 'B');
|
|
39
|
+
|
|
40
|
+
// Verify Interceptor 2 received args modified by Interceptor 1
|
|
41
|
+
expect(beforeInterceptor2).toHaveBeenCalledWith('A_mod1', 'B_mod1');
|
|
42
|
+
|
|
43
|
+
// Verify Interceptor 3 received args from Interceptor 1 (since Interceptor 2 returned null)
|
|
44
|
+
expect(beforeInterceptor3).toHaveBeenCalledWith('A_mod1', 'B_mod1');
|
|
45
|
+
|
|
46
|
+
// Verify Original received args modified by Interceptor 3
|
|
47
|
+
expect(originalFn).toHaveBeenCalledWith('A_mod1_mod3', 'B_mod1_mod3');
|
|
48
|
+
|
|
49
|
+
expect(result).toBe('Original: A_mod1_mod3, B_mod1_mod3');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('Around interceptors: should wrap execution and modify args/result', async () => {
|
|
53
|
+
const targetName = 'Test::aroundChain';
|
|
54
|
+
const originalFn = jest.fn((arg) => `Original(${arg})`);
|
|
55
|
+
const context = {};
|
|
56
|
+
|
|
57
|
+
// Outer Around Interceptor
|
|
58
|
+
const aroundInterceptor1 = jest.fn(async (proceed, arg) => {
|
|
59
|
+
const result = await proceed(arg + '_outerIn');
|
|
60
|
+
return `Outer(${result})`;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Inner Around Interceptor
|
|
64
|
+
const aroundInterceptor2 = jest.fn(async (proceed, arg) => {
|
|
65
|
+
const result = await proceed(arg + '_innerIn');
|
|
66
|
+
return `Inner(${result})`;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
interceptorManager.addInterceptor(targetName, 'Interceptor1', 'around', aroundInterceptor1, 10);
|
|
70
|
+
interceptorManager.addInterceptor(targetName, 'Interceptor2', 'around', aroundInterceptor2, 20);
|
|
71
|
+
|
|
72
|
+
const result = await interceptorManager.execute(targetName, originalFn, context, 'Start');
|
|
73
|
+
|
|
74
|
+
// Execution flow:
|
|
75
|
+
// Interceptor1 (Start) -> calls proceed('Start_outerIn')
|
|
76
|
+
// -> Interceptor2 ('Start_outerIn') -> calls proceed('Start_outerIn_innerIn')
|
|
77
|
+
// -> Original ('Start_outerIn_innerIn') -> returns 'Original(Start_outerIn_innerIn)'
|
|
78
|
+
// -> Interceptor2 returns 'Inner(Original(Start_outerIn_innerIn))'
|
|
79
|
+
// -> Interceptor1 returns 'Outer(Inner(Original(Start_outerIn_innerIn)))'
|
|
80
|
+
|
|
81
|
+
expect(originalFn).toHaveBeenCalledWith('Start_outerIn_innerIn');
|
|
82
|
+
expect(result).toBe('Outer(Inner(Original(Start_outerIn_innerIn)))');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('After interceptors: should chain results', async () => {
|
|
86
|
+
const targetName = 'Test::afterChain';
|
|
87
|
+
const originalFn = jest.fn(() => 'Original');
|
|
88
|
+
const context = {};
|
|
89
|
+
|
|
90
|
+
const afterInterceptor1 = jest.fn((result) => {
|
|
91
|
+
return result + '_After1';
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const afterInterceptor2 = jest.fn((result) => {
|
|
95
|
+
return result + '_After2';
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
interceptorManager.addInterceptor(targetName, 'Interceptor1', 'after', afterInterceptor1, 10);
|
|
99
|
+
interceptorManager.addInterceptor(targetName, 'Interceptor2', 'after', afterInterceptor2, 20);
|
|
100
|
+
|
|
101
|
+
const result = await interceptorManager.execute(targetName, originalFn, context);
|
|
102
|
+
|
|
103
|
+
expect(afterInterceptor1).toHaveBeenCalledWith('Original');
|
|
104
|
+
expect(afterInterceptor2).toHaveBeenCalledWith('Original_After1');
|
|
105
|
+
expect(result).toBe('Original_After1_After2');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('Full Chain: Before -> Around -> Original -> After', async () => {
|
|
109
|
+
const targetName = 'Test::fullChain';
|
|
110
|
+
const originalFn = jest.fn((arg) => `Original(${arg})`);
|
|
111
|
+
const context = {};
|
|
112
|
+
|
|
113
|
+
// Before: Modifies arg
|
|
114
|
+
interceptorManager.addInterceptor(targetName, 'Before', 'before', (arg) => [arg + '_Before'], 10);
|
|
115
|
+
|
|
116
|
+
// Around: Wraps and modifies arg further
|
|
117
|
+
interceptorManager.addInterceptor(targetName, 'Around', 'around', async (proceed, arg) => {
|
|
118
|
+
const res = await proceed(arg + '_AroundIn');
|
|
119
|
+
return `Around(${res})`;
|
|
120
|
+
}, 20);
|
|
121
|
+
|
|
122
|
+
// After: Modifies result
|
|
123
|
+
interceptorManager.addInterceptor(targetName, 'After', 'after', (res) => res + '_After', 30);
|
|
124
|
+
|
|
125
|
+
const result = await interceptorManager.execute(targetName, originalFn, context, 'Start');
|
|
126
|
+
|
|
127
|
+
// Flow:
|
|
128
|
+
// Before: Start -> Start_Before
|
|
129
|
+
// Around: Start_Before -> calls proceed(Start_Before_AroundIn)
|
|
130
|
+
// Original: Start_Before_AroundIn -> returns Original(Start_Before_AroundIn)
|
|
131
|
+
// Around: returns Around(Original(Start_Before_AroundIn))
|
|
132
|
+
// After: receives Around(...) -> returns Around(...)_After
|
|
133
|
+
|
|
134
|
+
expect(originalFn).toHaveBeenCalledWith('Start_Before_AroundIn');
|
|
135
|
+
expect(result).toBe('Around(Original(Start_Before_AroundIn))_After');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -12,90 +12,40 @@ let scenarios = [
|
|
|
12
12
|
{
|
|
13
13
|
code: 'Vendor/theme-a',
|
|
14
14
|
expected: {
|
|
15
|
-
tailwind: {
|
|
16
|
-
theme: { extend: {} },
|
|
17
|
-
plugins: [],
|
|
18
|
-
content: [
|
|
19
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-a/web/**/*.{vue,js}',
|
|
20
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-a/**/*.phtml',
|
|
21
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-a/*/page_layout/override/base/*.xml'
|
|
22
|
-
]
|
|
23
|
-
},
|
|
24
|
-
includeTailwindConfigFromParentThemes: true,
|
|
25
15
|
includeCssSourceFromParentThemes: true,
|
|
26
16
|
ignoredCssFromModules: [
|
|
27
17
|
'Vendor_ModuleNameA'
|
|
28
18
|
],
|
|
29
|
-
ignoredTailwindConfigFromModules: [],
|
|
30
19
|
exposeNpmPackages: []
|
|
31
20
|
}
|
|
32
21
|
},
|
|
33
22
|
{
|
|
34
23
|
code: 'Vendor/theme-b',
|
|
35
24
|
expected: {
|
|
36
|
-
tailwind: {
|
|
37
|
-
theme: {
|
|
38
|
-
extend: {
|
|
39
|
-
textColor: {
|
|
40
|
-
'primary': '#3490dc',
|
|
41
|
-
'secondary': '#ffed4a',
|
|
42
|
-
'danger': '#e3342f',
|
|
43
|
-
},
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
plugins: [],
|
|
47
|
-
content: [
|
|
48
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-a/web/**/*.{vue,js}',
|
|
49
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-a/**/*.phtml',
|
|
50
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-a/*/page_layout/override/base/*.xml',
|
|
51
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-b/web/**/*.{vue,js}',
|
|
52
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-b/**/*.phtml',
|
|
53
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-b/*/page_layout/override/base/*.xml'
|
|
54
|
-
]
|
|
55
|
-
},
|
|
56
|
-
includeTailwindConfigFromParentThemes: true,
|
|
57
25
|
includeCssSourceFromParentThemes: true,
|
|
58
26
|
ignoredCssFromModules: [
|
|
59
27
|
'Vendor_ModuleNameA',
|
|
60
28
|
'Vendor_ModuleNameNoConfig'
|
|
61
29
|
],
|
|
62
|
-
ignoredTailwindConfigFromModules: [],
|
|
63
30
|
exposeNpmPackages: []
|
|
64
31
|
}
|
|
65
32
|
},
|
|
66
33
|
{
|
|
67
34
|
code: 'Vendor/theme-c',
|
|
68
35
|
expected: {
|
|
69
|
-
tailwind: {
|
|
70
|
-
theme: { extend: {} },
|
|
71
|
-
plugins: [],
|
|
72
|
-
content: [
|
|
73
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-c/web/**/*.{vue,js}',
|
|
74
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-c/**/*.phtml',
|
|
75
|
-
'magento_scenarios/app/design/frontend/Vendor/theme-c/*/page_layout/override/base/*.xml'
|
|
76
|
-
]
|
|
77
|
-
},
|
|
78
|
-
includeTailwindConfigFromParentThemes: false,
|
|
79
36
|
includeCssSourceFromParentThemes: true,
|
|
80
|
-
ignoredCssFromModules: [
|
|
81
|
-
|
|
37
|
+
ignoredCssFromModules: [
|
|
38
|
+
'Vendor_ModuleNameA',
|
|
39
|
+
'Vendor_ModuleNameNoConfig'
|
|
40
|
+
],
|
|
82
41
|
exposeNpmPackages: []
|
|
83
42
|
}
|
|
84
43
|
},
|
|
85
44
|
{
|
|
86
45
|
code: 'Vendor/theme-d',
|
|
87
46
|
expected: {
|
|
88
|
-
tailwind: {
|
|
89
|
-
theme: { extend: {} },
|
|
90
|
-
plugins: [],
|
|
91
|
-
content: []
|
|
92
|
-
},
|
|
93
|
-
includeTailwindConfigFromParentThemes: true,
|
|
94
47
|
includeCssSourceFromParentThemes: false,
|
|
95
48
|
ignoredCssFromModules: [],
|
|
96
|
-
ignoredTailwindConfigFromModules: [
|
|
97
|
-
'Vendor_ModuleNameC'
|
|
98
|
-
],
|
|
99
49
|
exposeNpmPackages: []
|
|
100
50
|
}
|
|
101
51
|
}
|
|
@@ -107,7 +57,6 @@ for (const scenario of scenarios) {
|
|
|
107
57
|
for (const themeKey in scenario.themes) {
|
|
108
58
|
let theme = scenario.themes[themeKey];
|
|
109
59
|
const expected = theme.expected;
|
|
110
|
-
expected.tailwind.content = expected.tailwind.content?.map((content) => path.resolve(__dirname, '..', content));
|
|
111
60
|
}
|
|
112
61
|
}
|
|
113
62
|
|
|
@@ -145,24 +94,4 @@ describe('getThemeConfig', () => {
|
|
|
145
94
|
});
|
|
146
95
|
});
|
|
147
96
|
|
|
148
|
-
describe('getTailwindThemeConfig', () => {
|
|
149
|
-
let themeResolver;
|
|
150
|
-
|
|
151
|
-
beforeEach(() => {
|
|
152
|
-
jest.resetModules();
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test.each(scenarios.flatMap(({ scenario, themes }) =>
|
|
156
|
-
themes.map(({ code, expected }) => ([ scenario, code, expected ]))
|
|
157
|
-
))('Scenario "%s", Theme "%s"', async ( scenario, code, expected ) => {
|
|
158
|
-
themeResolver = await setupThemeResolver(scenario);
|
|
159
|
-
|
|
160
|
-
const result = await themeResolver.getTailwindThemeConfig(code);
|
|
161
|
-
|
|
162
|
-
expect(result).toMatchObject(
|
|
163
|
-
expected.tailwind,
|
|
164
|
-
`Expected:\n${JSON.stringify(expected.tailwind, null, 2)}\nReceived:\n${JSON.stringify(result, null, 2)}\nScenario: "${scenario}", Theme: "${code}"`
|
|
165
|
-
);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
97
|
|