Skip to content

Commit a731b47

Browse files
committed
Improve the script executor with support for more of the Phoebus API
1 parent 8668c99 commit a731b47

File tree

2 files changed

+247
-127
lines changed

2 files changed

+247
-127
lines changed
Lines changed: 165 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
1+
import { describe, it, expect, vi, beforeEach, afterEach, Mock } from "vitest";
22
import { iFrameScriptExecutionHandlerCode } from "./scriptExecutor";
3+
import { Procedure } from "@vitest/spy";
34

45
describe("iFrameScriptExecutionHandlerCode", () => {
56
let iframe: HTMLIFrameElement;
7+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
68
let messageEventListener: (event: MessageEvent) => void;
79

810
beforeEach(() => {
@@ -39,116 +41,177 @@ describe("iFrameScriptExecutionHandlerCode", () => {
3941
it("should execute simple dynamic code and return results via postMessage", async () => {
4042
const postMessageMock = vi.fn();
4143

42-
expect(iframe.contentWindow).not.toBeNull();
43-
if (iframe.contentWindow) {
44-
// Override the postMessage method on the iframe's parent
45-
Object.defineProperty(iframe.contentWindow, "parent", {
46-
value: {
47-
postMessage: postMessageMock
48-
},
49-
configurable: true
50-
});
51-
52-
const messageData = {
53-
functionCode: "return 40 + 2;",
54-
widget: {},
55-
PVs: []
56-
};
57-
58-
const messageEvent = new MessageEvent("message", {
59-
data: messageData,
60-
source: null
61-
});
62-
63-
// Send the code to the iframe via a message
64-
iframe.contentWindow.dispatchEvent(messageEvent);
65-
66-
// Short wait for async execution to complete
67-
await new Promise(resolve => setTimeout(resolve, 1000));
68-
69-
// Validate that the dynamic script executed as expected
70-
expect(postMessageMock).toHaveBeenCalledWith(42, "*");
71-
}
44+
const messageData = {
45+
functionCode: "return 40 + 2;",
46+
id: 1234,
47+
pvs: []
48+
};
49+
50+
await postTestMessageToIframe(iframe, postMessageMock, messageData);
51+
52+
// Validate that the dynamic script executed as expected
53+
expect(postMessageMock).toHaveBeenCalledWith(
54+
{ id: 1234, functionReturnValue: 42, widgetProps: {} },
55+
"*"
56+
);
7257
});
7358

7459
it("should execute code containing some Phoebus built ins and return results via postMessage", async () => {
7560
const postMessageMock = vi.fn();
61+
const testScript = `
62+
importClass(org.csstudio.display.builder.runtime.script.PVUtil);
63+
importClass(org.csstudio.display.builder.runtime.script.ScriptUtil);
64+
importPackage(Packages.org.csstudio.opibuilder.scriptUtil);
65+
return PVUtil.getDouble(1+2);
66+
`;
67+
68+
const messageData = {
69+
functionCode: testScript,
70+
id: 3456,
71+
pvs: []
72+
};
73+
74+
await postTestMessageToIframe(iframe, postMessageMock, messageData);
75+
76+
expect(postMessageMock).toHaveBeenCalledWith(
77+
{ id: 3456, functionReturnValue: 3, widgetProps: {} },
78+
"*"
79+
);
80+
});
7681

77-
expect(iframe.contentWindow).not.toBeNull();
78-
if (iframe.contentWindow) {
79-
// Override the postMessage method on the iframe's parent
80-
Object.defineProperty(iframe.contentWindow, "parent", {
81-
value: {
82-
postMessage: postMessageMock
83-
},
84-
configurable: true
85-
});
86-
87-
const script = `
88-
importClass(org.csstudio.display.builder.runtime.script.PVUtil);
89-
importClass(org.csstudio.display.builder.runtime.script.ScriptUtil);
90-
importPackage(Packages.org.csstudio.opibuilder.scriptUtil);
91-
92-
// widget.setPropertyValue("background_color", ColorFontUtil.getColorFromRGB(255, 255, 0));
93-
94-
return PVUtil.getDouble(1+2);
95-
`;
96-
97-
const messageData = {
98-
functionCode: script,
99-
widget: {},
100-
PVs: []
101-
};
102-
103-
const messageEvent = new MessageEvent("message", {
104-
data: messageData,
105-
source: null
106-
});
107-
108-
// Send the code to the iframe via a message
109-
iframe.contentWindow.dispatchEvent(messageEvent);
110-
111-
// Short wait for async execution to complete
112-
await new Promise(resolve => setTimeout(resolve, 1000));
113-
114-
// Validate that the dynamic script executed as expected
115-
expect(postMessageMock).toHaveBeenCalledWith(3, "*");
116-
}
82+
it("should execute code containing widget.setPropertyValue and return results via postMessage in widgetProps", async () => {
83+
const postMessageMock = vi.fn();
84+
const testScript = `
85+
widget.setPropertyValue("x", PVUtil.getDouble(1+2));
86+
widget.setPropertyValue("background_color", ColorFontUtil.getColorFromRGB(255, 0, 255));
87+
widget.setPropertyValue("foreground_color", ColorFontUtil.YELLOW);
88+
`;
89+
90+
const messageData = {
91+
functionCode: testScript,
92+
id: 73734,
93+
pvs: []
94+
};
95+
96+
await postTestMessageToIframe(iframe, postMessageMock, messageData);
97+
expect(postMessageMock).toHaveBeenCalledWith(
98+
{
99+
id: 73734,
100+
functionReturnValue: undefined,
101+
widgetProps: {
102+
x: 3,
103+
background_color: {
104+
text: "rgba(255,0,255,1)",
105+
type: "rgbaColor"
106+
},
107+
foreground_color: {
108+
text: "rgba(255,255,0,1)",
109+
type: "rgbaColor"
110+
}
111+
}
112+
},
113+
"*"
114+
);
115+
});
116+
117+
it("should execute code that uses a pv values and return results via postMessage in widgetProps", async () => {
118+
const postMessageMock = vi.fn();
119+
120+
const testScript = `
121+
const value = PVUtil.getDouble(pvs[0]);
122+
if (value > 299) {
123+
widget.setPropertyValue("background_color", ColorFontUtil.getColorFromRGB(255, 255, 0));
124+
} else {
125+
widget.setPropertyValue("background_color", ColorFontUtil.getColorFromRGB(128, 255, 255));
126+
}
127+
`;
128+
129+
const messageData1 = {
130+
functionCode: testScript,
131+
id: 73734,
132+
pvs: [301]
133+
};
134+
135+
const messageData2 = {
136+
functionCode: testScript,
137+
id: 9098,
138+
pvs: [298]
139+
};
140+
141+
await postTestMessageToIframe(iframe, postMessageMock, messageData1);
142+
await postTestMessageToIframe(iframe, postMessageMock, messageData2);
143+
144+
expect(postMessageMock).toHaveBeenCalledWith(
145+
{
146+
id: 73734,
147+
functionReturnValue: undefined,
148+
widgetProps: {
149+
background_color: {
150+
text: "rgba(255,255,0,1)",
151+
type: "rgbaColor"
152+
}
153+
}
154+
},
155+
"*"
156+
);
157+
158+
expect(postMessageMock).toHaveBeenCalledWith(
159+
{
160+
id: 9098,
161+
functionReturnValue: undefined,
162+
widgetProps: {
163+
background_color: {
164+
text: "rgba(128,255,255,1)",
165+
type: "rgbaColor"
166+
}
167+
}
168+
},
169+
"*"
170+
);
117171
});
118172

119173
it("should handle exception in the dynamic code and send error message via postMessage", async () => {
120174
const postMessageMock = vi.fn();
121175

122-
expect(iframe.contentWindow).not.toBeNull();
123-
if (iframe.contentWindow) {
124-
// Override the postMessage method on the iframe's parent
125-
Object.defineProperty(iframe.contentWindow, "parent", {
126-
value: {
127-
postMessage: postMessageMock
128-
},
129-
configurable: true
130-
});
131-
132-
const messageData = {
133-
functionCode: 'throw new Error("Test error");',
134-
widget: {},
135-
PVs: []
136-
};
137-
138-
// Create a test message event with invalid code
139-
const messageEvent = new MessageEvent("message", {
140-
data: messageData,
141-
source: null
142-
});
143-
144-
// Dispatch the event to the iframe's contentWindow
145-
iframe.contentWindow.dispatchEvent(messageEvent);
146-
147-
// Wait for async operations
148-
await new Promise(resolve => setTimeout(resolve, 1000));
149-
150-
// Check if postMessage was called with the expected error message
151-
expect(postMessageMock).toHaveBeenCalledWith("Error: Test error", "*");
152-
}
176+
const messageData = {
177+
functionCode: 'throw new Error("Test error");',
178+
id: 6789,
179+
pvs: []
180+
};
181+
182+
await postTestMessageToIframe(iframe, postMessageMock, messageData);
183+
184+
// Check if postMessage was called with the expected error message
185+
expect(postMessageMock).toHaveBeenCalledWith(
186+
{ id: 6789, error: "Error: Test error" },
187+
"*"
188+
);
153189
});
154190
});
191+
192+
const postTestMessageToIframe = async (
193+
iframe: HTMLIFrameElement,
194+
postMessageMock: Mock<Procedure>,
195+
messageData: { functionCode: string; id: number; pvs: any[] }
196+
) => {
197+
Object.defineProperty(iframe.contentWindow, "parent", {
198+
value: {
199+
postMessage: postMessageMock
200+
},
201+
configurable: true
202+
});
203+
204+
const messageEvent = new MessageEvent("message", {
205+
data: messageData,
206+
source: null
207+
});
208+
209+
expect(iframe.contentWindow).not.toBeNull();
210+
if (iframe.contentWindow) {
211+
// Send the code and pv values to the iframe via a message
212+
iframe.contentWindow.dispatchEvent(messageEvent);
213+
}
214+
215+
// Short wait for async execution to complete
216+
await new Promise(resolve => setTimeout(resolve, 200));
217+
};

0 commit comments

Comments
 (0)