node-red-contrib-event-calc 0.1.2 → 2.0.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/LICENSE +25 -0
- package/README.md +50 -51
- package/nodes/event-cache.js +84 -139
- package/nodes/event-calc.html +46 -22
- package/nodes/event-calc.js +82 -26
- package/nodes/event-chart.html +239 -0
- package/nodes/event-chart.js +106 -0
- package/nodes/event-json.html +58 -0
- package/nodes/event-json.js +69 -0
- package/nodes/event-simulator.html +156 -0
- package/nodes/event-simulator.js +185 -0
- package/nodes/event-topic.html +16 -30
- package/nodes/event-topic.js +33 -40
- package/package.json +24 -10
- package/playwright.config.js +22 -0
- package/tests/external-trigger.spec.js +141 -0
package/nodes/event-topic.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
defaults: {
|
|
11
11
|
name: { value: "" },
|
|
12
12
|
cache: { value: "", type: "event-cache", required: true },
|
|
13
|
-
|
|
13
|
+
topic: { value: "" },
|
|
14
14
|
outputFormat: { value: "value" },
|
|
15
15
|
outputOnStart: { value: false }
|
|
16
16
|
},
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
outputs: 1,
|
|
19
19
|
icon: "font-awesome/fa-filter",
|
|
20
20
|
label: function() {
|
|
21
|
-
return this.name || this.
|
|
21
|
+
return this.name || this.topic || "event topic";
|
|
22
22
|
},
|
|
23
23
|
paletteLabel: "event topic",
|
|
24
24
|
labelStyle: function() { return (this.name ? "node_label_italic" : "") + " event-calc-white-text"; },
|
|
@@ -30,18 +30,18 @@
|
|
|
30
30
|
const cacheId = $("#node-input-cache").val();
|
|
31
31
|
if (cacheId) {
|
|
32
32
|
$.getJSON("event-cache/" + cacheId + "/topics", function(topics) {
|
|
33
|
-
$("#node-input-
|
|
33
|
+
$("#node-input-topic").autocomplete("option", "source", topics || []);
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
// Setup autocomplete on
|
|
39
|
-
$("#node-input-
|
|
38
|
+
// Setup autocomplete on topic input
|
|
39
|
+
$("#node-input-topic").autocomplete({
|
|
40
40
|
source: [],
|
|
41
41
|
minLength: 0,
|
|
42
42
|
delay: 0
|
|
43
43
|
}).on("focus", function() {
|
|
44
|
-
if (!$(this).val()
|
|
44
|
+
if (!$(this).val()) {
|
|
45
45
|
$(this).autocomplete("search", "");
|
|
46
46
|
}
|
|
47
47
|
});
|
|
@@ -65,19 +65,14 @@
|
|
|
65
65
|
<input type="text" id="node-input-cache">
|
|
66
66
|
</div>
|
|
67
67
|
<div class="form-row">
|
|
68
|
-
<label for="node-input-
|
|
69
|
-
<input type="text" id="node-input-
|
|
70
|
-
<div class="form-tips">
|
|
71
|
-
Wildcards: <code>?</code> = exactly one character, <code>*</code> = one or more characters.<br>
|
|
72
|
-
Examples: <code>sensor?</code> matches sensor1, <code>sensors/*</code> matches sensors/temp
|
|
73
|
-
</div>
|
|
68
|
+
<label for="node-input-topic"><i class="fa fa-bookmark"></i> Topic</label>
|
|
69
|
+
<input type="text" id="node-input-topic" placeholder="sensors/room1/temp">
|
|
74
70
|
</div>
|
|
75
71
|
<div class="form-row">
|
|
76
72
|
<label for="node-input-outputFormat"><i class="fa fa-sign-out"></i> Output Format</label>
|
|
77
73
|
<select id="node-input-outputFormat" style="width:70%;">
|
|
78
74
|
<option value="value">Value only (msg.payload = value)</option>
|
|
79
75
|
<option value="full">Full entry (msg.payload = {value, ts, metadata})</option>
|
|
80
|
-
<option value="all">All matching (msg.payload = {topic: value, ...})</option>
|
|
81
76
|
</select>
|
|
82
77
|
</div>
|
|
83
78
|
<div class="form-row">
|
|
@@ -88,32 +83,31 @@
|
|
|
88
83
|
</script>
|
|
89
84
|
|
|
90
85
|
<script type="text/html" data-help-name="event-topic">
|
|
91
|
-
<p>Subscribes to a topic
|
|
86
|
+
<p>Subscribes to a topic and outputs when that topic updates in the cache.</p>
|
|
92
87
|
|
|
93
88
|
<h3>Properties</h3>
|
|
94
89
|
<dl class="message-properties">
|
|
95
90
|
<dt>Cache</dt>
|
|
96
91
|
<dd>The event-cache config node to use</dd>
|
|
97
|
-
<dt>Topic
|
|
98
|
-
<dd>
|
|
92
|
+
<dt>Topic</dt>
|
|
93
|
+
<dd>The exact topic to subscribe to</dd>
|
|
99
94
|
<dt>Output Format</dt>
|
|
100
95
|
<dd>
|
|
101
96
|
<ul>
|
|
102
97
|
<li><b>Value only</b>: <code>msg.payload</code> contains just the value</li>
|
|
103
98
|
<li><b>Full entry</b>: <code>msg.payload</code> contains <code>{value, ts, metadata}</code></li>
|
|
104
|
-
<li><b>All matching</b>: <code>msg.payload</code> contains all cached values matching the pattern</li>
|
|
105
99
|
</ul>
|
|
106
100
|
</dd>
|
|
107
101
|
<dt>Output on deploy</dt>
|
|
108
|
-
<dd>If checked, outputs
|
|
102
|
+
<dd>If checked, outputs the current cached value when the flow starts</dd>
|
|
109
103
|
</dl>
|
|
110
104
|
|
|
111
105
|
<h3>Inputs</h3>
|
|
112
106
|
<dl class="message-properties">
|
|
113
|
-
<dt class="optional">
|
|
114
|
-
<dd>Dynamically change the subscription
|
|
115
|
-
<dt class="optional">payload
|
|
116
|
-
<dd>Output
|
|
107
|
+
<dt class="optional">topic + payload="subscribe"</dt>
|
|
108
|
+
<dd>Dynamically change the subscription to a new topic</dd>
|
|
109
|
+
<dt class="optional">payload = "refresh"</dt>
|
|
110
|
+
<dd>Output the current cached value</dd>
|
|
117
111
|
</dl>
|
|
118
112
|
|
|
119
113
|
<h3>Outputs</h3>
|
|
@@ -125,12 +119,4 @@
|
|
|
125
119
|
<dt>timestamp <span class="property-type">number</span></dt>
|
|
126
120
|
<dd>Unix timestamp when the value was cached</dd>
|
|
127
121
|
</dl>
|
|
128
|
-
|
|
129
|
-
<h3>Wildcard Examples</h3>
|
|
130
|
-
<ul>
|
|
131
|
-
<li><code>sensor?</code> - matches sensor1, sensorA (exactly one char)</li>
|
|
132
|
-
<li><code>sensors/*</code> - matches sensors/temp, sensors/room1 (one or more chars after /)</li>
|
|
133
|
-
<li><code>*/temp</code> - matches room1/temp, sensors/temp</li>
|
|
134
|
-
<li><code>*</code> - matches any topic with one or more characters</li>
|
|
135
|
-
</ul>
|
|
136
122
|
</script>
|
package/nodes/event-topic.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* event-topic - Subscription node for
|
|
2
|
+
* event-topic - Subscription node for exact topics
|
|
3
3
|
*
|
|
4
4
|
* Features:
|
|
5
|
-
* - Subscribes to cache
|
|
6
|
-
* - Outputs when
|
|
7
|
-
* - Multiple output formats: value only
|
|
8
|
-
* - Optional output of existing
|
|
9
|
-
* - Dynamic
|
|
5
|
+
* - Subscribes to cache for a specific topic
|
|
6
|
+
* - Outputs when the topic updates
|
|
7
|
+
* - Multiple output formats: value only or full entry
|
|
8
|
+
* - Optional output of existing value on start
|
|
9
|
+
* - Dynamic topic change via input message
|
|
10
10
|
*/
|
|
11
11
|
module.exports = function(RED) {
|
|
12
12
|
function EventTopicNode(config) {
|
|
@@ -14,7 +14,8 @@ module.exports = function(RED) {
|
|
|
14
14
|
const node = this;
|
|
15
15
|
|
|
16
16
|
node.cacheConfig = RED.nodes.getNode(config.cache);
|
|
17
|
-
|
|
17
|
+
// Support both old 'pattern' and new 'topic' config
|
|
18
|
+
node.topic = config.topic || config.pattern || '';
|
|
18
19
|
node.outputFormat = config.outputFormat || 'value';
|
|
19
20
|
node.outputOnStart = config.outputOnStart || false;
|
|
20
21
|
|
|
@@ -25,6 +26,11 @@ module.exports = function(RED) {
|
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
if (!node.topic) {
|
|
30
|
+
node.status({ fill: "yellow", shape: "ring", text: "no topic" });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
/**
|
|
29
35
|
* Build output message based on configured format
|
|
30
36
|
*/
|
|
@@ -45,21 +51,6 @@ module.exports = function(RED) {
|
|
|
45
51
|
metadata: entry.metadata
|
|
46
52
|
}
|
|
47
53
|
};
|
|
48
|
-
case 'all':
|
|
49
|
-
const all = node.cacheConfig.getMatching(node.pattern);
|
|
50
|
-
const values = {};
|
|
51
|
-
for (const [t, e] of all) {
|
|
52
|
-
values[t] = e.value;
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
topic: topic,
|
|
56
|
-
payload: values,
|
|
57
|
-
trigger: {
|
|
58
|
-
topic: topic,
|
|
59
|
-
value: entry.value
|
|
60
|
-
},
|
|
61
|
-
timestamp: entry.ts
|
|
62
|
-
};
|
|
63
54
|
default:
|
|
64
55
|
return {
|
|
65
56
|
topic: topic,
|
|
@@ -69,10 +60,10 @@ module.exports = function(RED) {
|
|
|
69
60
|
}
|
|
70
61
|
|
|
71
62
|
/**
|
|
72
|
-
* Subscribe to the current
|
|
63
|
+
* Subscribe to the current topic
|
|
73
64
|
*/
|
|
74
65
|
function subscribe() {
|
|
75
|
-
subscriptionId = node.cacheConfig.subscribe(node.
|
|
66
|
+
subscriptionId = node.cacheConfig.subscribe(node.topic, (topic, entry) => {
|
|
76
67
|
const msg = buildOutputMessage(topic, entry);
|
|
77
68
|
node.send(msg);
|
|
78
69
|
|
|
@@ -84,43 +75,45 @@ module.exports = function(RED) {
|
|
|
84
75
|
|
|
85
76
|
// Initial subscription
|
|
86
77
|
subscribe();
|
|
87
|
-
node.
|
|
78
|
+
const displayTopic = node.topic.length > 20 ? node.topic.substring(0, 17) + '...' : node.topic;
|
|
79
|
+
node.status({ fill: "green", shape: "dot", text: displayTopic });
|
|
88
80
|
|
|
89
|
-
// Output existing
|
|
81
|
+
// Output existing value on start if configured
|
|
90
82
|
if (node.outputOnStart) {
|
|
91
83
|
setImmediate(() => {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
const msg = buildOutputMessage(topic, entry);
|
|
84
|
+
const entry = node.cacheConfig.getValue(node.topic);
|
|
85
|
+
if (entry) {
|
|
86
|
+
const msg = buildOutputMessage(node.topic, entry);
|
|
95
87
|
node.send(msg);
|
|
96
88
|
}
|
|
97
89
|
});
|
|
98
90
|
}
|
|
99
91
|
|
|
100
|
-
// Handle input messages for dynamic
|
|
92
|
+
// Handle input messages for dynamic topic change
|
|
101
93
|
node.on('input', function(msg, send, done) {
|
|
102
94
|
// For Node-RED 0.x compatibility
|
|
103
95
|
send = send || function() { node.send.apply(node, arguments); };
|
|
104
96
|
done = done || function(err) { if (err) node.error(err, msg); };
|
|
105
97
|
|
|
106
|
-
if (msg.
|
|
107
|
-
// Unsubscribe from old
|
|
98
|
+
if (msg.topic && typeof msg.topic === 'string' && msg.payload === 'subscribe') {
|
|
99
|
+
// Unsubscribe from old topic
|
|
108
100
|
if (subscriptionId && node.cacheConfig) {
|
|
109
101
|
node.cacheConfig.unsubscribe(subscriptionId);
|
|
110
102
|
}
|
|
111
103
|
|
|
112
|
-
// Update
|
|
113
|
-
node.
|
|
104
|
+
// Update topic and resubscribe
|
|
105
|
+
node.topic = msg.topic;
|
|
114
106
|
subscribe();
|
|
115
107
|
|
|
116
|
-
node.
|
|
108
|
+
const dt = node.topic.length > 20 ? node.topic.substring(0, 17) + '...' : node.topic;
|
|
109
|
+
node.status({ fill: "blue", shape: "dot", text: dt });
|
|
117
110
|
}
|
|
118
111
|
|
|
119
|
-
// Allow manual trigger to output
|
|
120
|
-
if (msg.
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
const outMsg = buildOutputMessage(topic, entry);
|
|
112
|
+
// Allow manual trigger to output current value
|
|
113
|
+
if (msg.payload === 'refresh') {
|
|
114
|
+
const entry = node.cacheConfig.getValue(node.topic);
|
|
115
|
+
if (entry) {
|
|
116
|
+
const outMsg = buildOutputMessage(node.topic, entry);
|
|
124
117
|
send(outMsg);
|
|
125
118
|
}
|
|
126
119
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-event-calc",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Node-RED nodes for event caching and calculations
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Node-RED nodes for event caching and calculations",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Holger Amort"
|
|
7
7
|
},
|
|
@@ -9,19 +9,18 @@
|
|
|
9
9
|
"node-red",
|
|
10
10
|
"events",
|
|
11
11
|
"cache",
|
|
12
|
-
"wildcards",
|
|
13
12
|
"reactive",
|
|
14
13
|
"calculation",
|
|
15
14
|
"streaming"
|
|
16
15
|
],
|
|
17
|
-
"license": "
|
|
18
|
-
"homepage": "https://github.com/ErnstHolger/node-red#readme",
|
|
16
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
17
|
+
"homepage": "https://github.com/ErnstHolger/node-red-contrib-event-calc#readme",
|
|
19
18
|
"bugs": {
|
|
20
|
-
"url": "https://github.com/ErnstHolger/node-red/issues"
|
|
19
|
+
"url": "https://github.com/ErnstHolger/node-red-contrib-event-calc/issues"
|
|
21
20
|
},
|
|
22
21
|
"repository": {
|
|
23
22
|
"type": "git",
|
|
24
|
-
"url": "git+https://github.com/ErnstHolger/node-red.git"
|
|
23
|
+
"url": "git+https://github.com/ErnstHolger/node-red-contrib-event-calc.git"
|
|
25
24
|
},
|
|
26
25
|
"node-red": {
|
|
27
26
|
"version": ">=2.0.0",
|
|
@@ -29,14 +28,29 @@
|
|
|
29
28
|
"event-cache": "nodes/event-cache.js",
|
|
30
29
|
"event-in": "nodes/event-in.js",
|
|
31
30
|
"event-topic": "nodes/event-topic.js",
|
|
32
|
-
"event-calc": "nodes/event-calc.js"
|
|
31
|
+
"event-calc": "nodes/event-calc.js",
|
|
32
|
+
"event-json": "nodes/event-json.js",
|
|
33
|
+
"event-simulator": "nodes/event-simulator.js",
|
|
34
|
+
"event-chart": "nodes/event-chart.js"
|
|
33
35
|
}
|
|
34
36
|
},
|
|
35
37
|
"engines": {
|
|
36
38
|
"node": ">=18.0.0"
|
|
37
39
|
},
|
|
38
40
|
"scripts": {
|
|
39
|
-
"
|
|
41
|
+
"deploy": "npm link",
|
|
42
|
+
"deploy:patch": "npm version patch --no-git-tag-version && npm link",
|
|
43
|
+
"deploy:minor": "npm version minor --no-git-tag-version && npm link",
|
|
44
|
+
"deploy:major": "npm version major --no-git-tag-version && npm link",
|
|
45
|
+
"publish:dry": "npm publish --dry-run",
|
|
46
|
+
"publish:patch": "npm version patch && npm publish && start https://flows.nodered.org/add/node",
|
|
47
|
+
"publish:minor": "npm version minor && npm publish && start https://flows.nodered.org/add/node",
|
|
48
|
+
"publish:major": "npm version major && npm publish && start https://flows.nodered.org/add/node",
|
|
49
|
+
"test": "playwright test",
|
|
50
|
+
"test:ui": "playwright test --ui",
|
|
51
|
+
"test:headed": "playwright test --headed"
|
|
40
52
|
},
|
|
41
|
-
"
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@playwright/test": "^1.57.0"
|
|
55
|
+
}
|
|
42
56
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const { defineConfig } = require('@playwright/test');
|
|
3
|
+
|
|
4
|
+
module.exports = defineConfig({
|
|
5
|
+
testDir: './tests',
|
|
6
|
+
fullyParallel: false,
|
|
7
|
+
forbidOnly: !!process.env.CI,
|
|
8
|
+
retries: process.env.CI ? 2 : 0,
|
|
9
|
+
workers: 1,
|
|
10
|
+
reporter: 'html',
|
|
11
|
+
use: {
|
|
12
|
+
baseURL: 'http://localhost:1880',
|
|
13
|
+
trace: 'on-first-retry',
|
|
14
|
+
screenshot: 'only-on-failure',
|
|
15
|
+
},
|
|
16
|
+
projects: [
|
|
17
|
+
{
|
|
18
|
+
name: 'chromium',
|
|
19
|
+
use: { browserName: 'chromium' },
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const { test, expect } = require('@playwright/test');
|
|
3
|
+
|
|
4
|
+
test.describe('Event Calc - External Trigger Feature', () => {
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
// Navigate to Node-RED editor
|
|
7
|
+
await page.goto('/');
|
|
8
|
+
// Wait for Node-RED to load
|
|
9
|
+
await page.waitForSelector('#red-ui-palette', { timeout: 30000 });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('should show External Trigger checkbox in event-calc node configuration', async ({ page }) => {
|
|
13
|
+
// Search for the event-calc node in the palette
|
|
14
|
+
const paletteSearch = page.locator('#red-ui-palette-search input');
|
|
15
|
+
await paletteSearch.fill('event calc');
|
|
16
|
+
|
|
17
|
+
// Wait for search results
|
|
18
|
+
await page.waitForTimeout(500);
|
|
19
|
+
|
|
20
|
+
// Find the event-calc node in the palette
|
|
21
|
+
const eventCalcNode = page.locator('.red-ui-palette-node[data-palette-type="event-calc"]');
|
|
22
|
+
await expect(eventCalcNode).toBeVisible();
|
|
23
|
+
|
|
24
|
+
// Drag the node to the workspace
|
|
25
|
+
const workspace = page.locator('#red-ui-workspace-chart');
|
|
26
|
+
await eventCalcNode.dragTo(workspace);
|
|
27
|
+
|
|
28
|
+
// Double-click on the newly created node to open the editor
|
|
29
|
+
// First find the node in the workspace
|
|
30
|
+
const nodeInWorkspace = page.locator('.red-ui-flow-node-group').last();
|
|
31
|
+
await nodeInWorkspace.dblclick();
|
|
32
|
+
|
|
33
|
+
// Wait for the edit dialog to open
|
|
34
|
+
await page.waitForSelector('.red-ui-editor', { timeout: 5000 });
|
|
35
|
+
|
|
36
|
+
// Verify the External Trigger checkbox exists
|
|
37
|
+
const externalTriggerCheckbox = page.locator('#node-input-externalTrigger');
|
|
38
|
+
await expect(externalTriggerCheckbox).toBeVisible();
|
|
39
|
+
|
|
40
|
+
// Verify it's unchecked by default
|
|
41
|
+
await expect(externalTriggerCheckbox).not.toBeChecked();
|
|
42
|
+
|
|
43
|
+
// Check the checkbox
|
|
44
|
+
await externalTriggerCheckbox.check();
|
|
45
|
+
await expect(externalTriggerCheckbox).toBeChecked();
|
|
46
|
+
|
|
47
|
+
// Verify the label is correct
|
|
48
|
+
const label = page.locator('label[for="node-input-externalTrigger"]');
|
|
49
|
+
await expect(label).toContainText('External Trigger');
|
|
50
|
+
await expect(label).toContainText('calculate on any input message');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should toggle External Trigger checkbox', async ({ page }) => {
|
|
54
|
+
// Search for the event-calc node
|
|
55
|
+
const paletteSearch = page.locator('#red-ui-palette-search input');
|
|
56
|
+
await paletteSearch.fill('event calc');
|
|
57
|
+
await page.waitForTimeout(500);
|
|
58
|
+
|
|
59
|
+
// Find and drag the node
|
|
60
|
+
const eventCalcNode = page.locator('.red-ui-palette-node[data-palette-type="event-calc"]');
|
|
61
|
+
const workspace = page.locator('#red-ui-workspace-chart');
|
|
62
|
+
await eventCalcNode.dragTo(workspace);
|
|
63
|
+
|
|
64
|
+
// Open the node editor
|
|
65
|
+
const nodeInWorkspace = page.locator('.red-ui-flow-node-group').last();
|
|
66
|
+
await nodeInWorkspace.dblclick();
|
|
67
|
+
await page.waitForSelector('.red-ui-editor', { timeout: 5000 });
|
|
68
|
+
|
|
69
|
+
const externalTriggerCheckbox = page.locator('#node-input-externalTrigger');
|
|
70
|
+
|
|
71
|
+
// Toggle on
|
|
72
|
+
await externalTriggerCheckbox.check();
|
|
73
|
+
await expect(externalTriggerCheckbox).toBeChecked();
|
|
74
|
+
|
|
75
|
+
// Toggle off
|
|
76
|
+
await externalTriggerCheckbox.uncheck();
|
|
77
|
+
await expect(externalTriggerCheckbox).not.toBeChecked();
|
|
78
|
+
|
|
79
|
+
// Toggle on again
|
|
80
|
+
await externalTriggerCheckbox.check();
|
|
81
|
+
await expect(externalTriggerCheckbox).toBeChecked();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('should persist External Trigger setting after save', async ({ page }) => {
|
|
85
|
+
// Search for event-cache node first (required config)
|
|
86
|
+
const paletteSearch = page.locator('#red-ui-palette-search input');
|
|
87
|
+
await paletteSearch.fill('event cache');
|
|
88
|
+
await page.waitForTimeout(500);
|
|
89
|
+
|
|
90
|
+
// Add event-cache config node if needed
|
|
91
|
+
const eventCacheNode = page.locator('.red-ui-palette-node[data-palette-type="event-cache"]');
|
|
92
|
+
if (await eventCacheNode.isVisible()) {
|
|
93
|
+
const workspace = page.locator('#red-ui-workspace-chart');
|
|
94
|
+
await eventCacheNode.dragTo(workspace);
|
|
95
|
+
|
|
96
|
+
// Configure the cache node
|
|
97
|
+
const cacheNodeInWorkspace = page.locator('.red-ui-flow-node-group').last();
|
|
98
|
+
await cacheNodeInWorkspace.dblclick();
|
|
99
|
+
await page.waitForSelector('.red-ui-editor', { timeout: 5000 });
|
|
100
|
+
|
|
101
|
+
// Set a name for the cache
|
|
102
|
+
const nameInput = page.locator('#node-input-name');
|
|
103
|
+
await nameInput.fill('Test Cache');
|
|
104
|
+
|
|
105
|
+
// Save the cache node
|
|
106
|
+
const doneButton = page.locator('.red-ui-tray-footer button').filter({ hasText: 'Done' });
|
|
107
|
+
await doneButton.click();
|
|
108
|
+
await page.waitForTimeout(500);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Now add the event-calc node
|
|
112
|
+
await paletteSearch.clear();
|
|
113
|
+
await paletteSearch.fill('event calc');
|
|
114
|
+
await page.waitForTimeout(500);
|
|
115
|
+
|
|
116
|
+
const eventCalcNode = page.locator('.red-ui-palette-node[data-palette-type="event-calc"]');
|
|
117
|
+
const workspace = page.locator('#red-ui-workspace-chart');
|
|
118
|
+
await eventCalcNode.dragTo(workspace);
|
|
119
|
+
|
|
120
|
+
// Open the node editor
|
|
121
|
+
const calcNodeInWorkspace = page.locator('.red-ui-flow-node-group').last();
|
|
122
|
+
await calcNodeInWorkspace.dblclick();
|
|
123
|
+
await page.waitForSelector('.red-ui-editor', { timeout: 5000 });
|
|
124
|
+
|
|
125
|
+
// Check the External Trigger checkbox
|
|
126
|
+
const externalTriggerCheckbox = page.locator('#node-input-externalTrigger');
|
|
127
|
+
await externalTriggerCheckbox.check();
|
|
128
|
+
|
|
129
|
+
// Save the node configuration
|
|
130
|
+
const doneButton = page.locator('.red-ui-tray-footer button').filter({ hasText: 'Done' });
|
|
131
|
+
await doneButton.click();
|
|
132
|
+
await page.waitForTimeout(500);
|
|
133
|
+
|
|
134
|
+
// Reopen the node editor
|
|
135
|
+
await calcNodeInWorkspace.dblclick();
|
|
136
|
+
await page.waitForSelector('.red-ui-editor', { timeout: 5000 });
|
|
137
|
+
|
|
138
|
+
// Verify the checkbox is still checked
|
|
139
|
+
await expect(externalTriggerCheckbox).toBeChecked();
|
|
140
|
+
});
|
|
141
|
+
});
|