Skip to content

Commit 858a8d0

Browse files
committed
fix(builder): JSDoc: avoid errors when symbol has no events
This is a fix for a bug which initially has been fixed in v4 (SAP/ui5-builder#1162) but not upported to v5 yet. This fix also resolves the error occurence found when running `ui5 build jsdoc` in the UI5/sample-app. This will add a unit test for transformApiJson.js covering this scenario.
1 parent 1b1a00c commit 858a8d0

File tree

2 files changed

+207
-2
lines changed

2 files changed

+207
-2
lines changed

packages/builder/lib/processors/jsdoc/lib/transformApiJson.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,8 +657,8 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
657657
Object.keys(aParams).forEach((sParam) => {
658658
const sSince = aParams[sParam].since;
659659
const oDeprecated = aParams[sParam].deprecated;
660-
const oEvtInSymbol = oSymbol.events.find((e) => e.name === oEvent.name);
661-
const oParamInSymbol = oEvtInSymbol && oEvtInSymbol.parameters[0] &&
660+
const oEvtInSymbol = oSymbol.events?.find((e) => e.name === oEvent.name);
661+
const oParamInSymbol = oEvtInSymbol && oEvtInSymbol.parameters && oEvtInSymbol.parameters[0] &&
662662
oEvtInSymbol.parameters[0].parameterProperties &&
663663
oEvtInSymbol.parameters[0].parameterProperties.getParameters &&
664664
oEvtInSymbol.parameters[0].parameterProperties.getParameters.parameterProperties &&
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import test from "ava";
2+
import transformApiJson from "../../../../../lib/processors/jsdoc/lib/transformApiJson.js";
3+
4+
// Creates a mock fs object for testing transformApiJson:
5+
function createMockFs(fileContents, outputCapture) {
6+
const getFileContent = (filePath) => {
7+
// Find matching content based on file pattern
8+
for (const [pattern, content] of Object.entries(fileContents)) {
9+
if (filePath.includes(pattern)) {
10+
return typeof content === "string" ? content : JSON.stringify(content);
11+
}
12+
}
13+
throw new Error(`Unexpected file read: ${filePath}`);
14+
};
15+
16+
return {
17+
readFileSync: (filePath) => {
18+
return getFileContent(filePath);
19+
},
20+
readFile: (filePath, encoding, callback) => {
21+
try {
22+
const content = getFileContent(filePath);
23+
callback(null, content);
24+
} catch (error) {
25+
callback(error, null);
26+
}
27+
},
28+
writeFileSync: (filePath, content) => {
29+
outputCapture.content = content;
30+
},
31+
mkdirSync: () => {
32+
// No-op for mock
33+
},
34+
existsSync: (filePath) => {
35+
// Check if any pattern matches
36+
return Object.keys(fileContents).some((pattern) => filePath.includes(pattern));
37+
},
38+
readdirSync: () => {
39+
return [];
40+
},
41+
statSync: () => {
42+
return {isDirectory: () => false};
43+
}
44+
};
45+
}
46+
47+
test("transformApiJson: handles symbol without / with events property", async (t) => {
48+
/* ----------------------------------------------------------------------------------------------
49+
TEST 1: Mock api.json with a symbol that has:
50+
oSymbol.events undefined (the special scenario --> occurs in seamless wrappers for webcomponents)
51+
Initially found here: https://github.com/SAP/ui5-builder/pull/1162
52+
---------------------------------------------------------------------------------------------
53+
*/
54+
const mockApiJson = {
55+
"library": "test.library",
56+
"symbols": [
57+
{
58+
"kind": "class",
59+
"name": "test.library.TestClass",
60+
"module": "test/library/TestClass",
61+
"visibility": "public",
62+
"description": "Test class without events property on symbol",
63+
"ui5-metadata": {
64+
"stereotype": "control",
65+
"properties": [
66+
{
67+
"name": "testProp",
68+
"visibility": "public",
69+
"description": "test",
70+
}
71+
],
72+
"events": [
73+
{
74+
"name": "testEvent",
75+
"visibility": "public",
76+
"description": "Test event",
77+
"parameters": {
78+
"param1": {
79+
"name": "param1",
80+
"type": "string",
81+
"description": "Test parameter"
82+
}
83+
}
84+
}
85+
]
86+
}
87+
// Note: Intentionally missing "events" property at symbol level
88+
}
89+
]
90+
};
91+
92+
const mockLibraryXml = `<?xml version="1.0" encoding="UTF-8" ?>
93+
<library xmlns="http://www.sap.com/sap.ui.library.xsd">
94+
<name>test.library</name>
95+
<vendor>SAP SE</vendor>
96+
<version>1.0.0</version>
97+
</library>`;
98+
99+
const outputCapture = {content: null};
100+
const mockFs = createMockFs({
101+
"api.json": mockApiJson,
102+
".library": mockLibraryXml
103+
}, outputCapture);
104+
105+
await transformApiJson(
106+
"/mock/path/api.json",
107+
"/mock/path/api-processed.json",
108+
"/mock/path/.library",
109+
[],
110+
undefined,
111+
{fs: mockFs}
112+
);
113+
114+
t.truthy(outputCapture.content, "Output file was written");
115+
116+
const result = JSON.parse(outputCapture.content);
117+
t.truthy(result.symbols, "Symbols array exists in output");
118+
t.true(result.symbols.length >= 1, "At least one symbol processed");
119+
120+
// The main goal: verify no error was thrown when processing a symbol
121+
// that has ui5-metadata.events but no events property on the symbol itself
122+
const processedSymbol = result.symbols.find((s) => s.name === "test.library.TestClass");
123+
t.truthy(processedSymbol, "Test symbol was successfully processed without throwing an error");
124+
125+
126+
/* ----------------------------------------------------------------------------------------------
127+
TEST 2: Mock api.json with a symbol that has:
128+
oSymbol.events defined (the usual scenario)
129+
---------------------------------------------------------------------------------------------
130+
*/
131+
const mockApiJson2 = {
132+
"library": "test.library",
133+
"symbols": [
134+
{
135+
"kind": "class",
136+
"name": "test.library.TestClass",
137+
"module": "test/library/TestClass",
138+
"visibility": "public",
139+
"description": "Test class without events property on symbol",
140+
"ui5-metadata": {
141+
"stereotype": "control",
142+
"properties": [
143+
{
144+
"name": "testProp",
145+
"visibility": "public",
146+
"description": "test",
147+
}
148+
],
149+
"events": [
150+
{
151+
"name": "testEvent",
152+
"visibility": "public",
153+
"description": "Test event",
154+
"parameters": {
155+
"param1": {
156+
"name": "param1",
157+
"type": "string",
158+
"description": "Test parameter"
159+
}
160+
}
161+
}
162+
]
163+
},
164+
"events": [ // This is the usual scenario where events are defined at symbol level as well
165+
{
166+
"name": "testEvent",
167+
"visibility": "public",
168+
"description": "Test event at symbol level",
169+
"parameters": [
170+
{
171+
"name": "testParam",
172+
"type": "testParamType",
173+
}
174+
],
175+
}
176+
]
177+
}
178+
]
179+
};
180+
181+
const outputCapture2 = {content: null};
182+
const mockFs2 = createMockFs({
183+
"api.json": mockApiJson2,
184+
".library": mockLibraryXml
185+
}, outputCapture2);
186+
187+
await transformApiJson(
188+
"/mock/path/api.json",
189+
"/mock/path/api-processed.json",
190+
"/mock/path/.library",
191+
[],
192+
undefined,
193+
{fs: mockFs2}
194+
);
195+
196+
t.truthy(outputCapture2.content, "Output file was written");
197+
198+
const result2 = JSON.parse(outputCapture2.content);
199+
t.truthy(result2.symbols, "Symbols array exists in output");
200+
t.true(result2.symbols.length >= 1, "At least one symbol processed");
201+
202+
// Verify still no error was thrown (when using a symbol with events defined)
203+
const processedSymbol2 = result2.symbols.find((s) => s.name === "test.library.TestClass");
204+
t.truthy(processedSymbol2, "Test symbol was successfully processed without throwing an error");
205+
});

0 commit comments

Comments
 (0)