Skip to content

Conversation

@cstns
Copy link
Contributor

@cstns cstns commented Jan 19, 2026

Description

Fix ui-button payload date format type selection

Related Issue(s)

closes #2018

Checklist

  • I have read the contribution guidelines
  • Suitable unit/system level tests have been added and they pass
  • Documentation has been updated
    • Upgrade instructions
    • Configuration details
    • Concepts
  • Changes flowforge.yml?
    • Issue/PR raised on FlowFuse/helm to update ConfigMap Template
    • Issue/PR raised on FlowFuse/CloudProject to update values for Staging/Production
  • Link to Changelog Entry PR, or note why one is not needed.

Labels

  • Includes a DB migration? -> add the area:migration label

@cstns cstns self-assigned this Jan 19, 2026
@cstns cstns requested a review from Steve-Mcl January 19, 2026 16:06
Copy link
Contributor

@Steve-Mcl Steve-Mcl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add tests. Here are some I prepared ealier...

test/nodes/widgets/ui_button.spec.js

near the top (in scope)

/** utility to wait for an event and return a promise */
function on (emitter, event) {
    return new Promise((resolve) => {
        emitter.on(event, resolve)
    })
}

towards the end of the test file

    it('should receive an iso date in payload', async function () {
        const copyOfFlow = JSON.parse(JSON.stringify(flow))
        const buttonDefinition = copyOfFlow.find(n => n.id === 'node-ui-button')
        buttonDefinition.payload = 'iso'
        buttonDefinition.payloadType = 'date'
        await helper.load(nodeImports, copyOfFlow)
        verifyFlowLoaded(helper, copyOfFlow)
        const base = helper.getNode('config-ui-base')

        // setup a fake connection so that button has somewhere to emit to and we can spy on it
        const socket = {
            id: 'fake-conn-id',
            emit: sinon.spy()
        }
        base.uiShared.connections['fake-conn-id'] = socket // fake connection so that button has somewhere to emit

        // send a message to the button node
        const button = helper.getNode('node-ui-button')
        const hNode = helper.getNode('helper-node')
        const inputPromise = on(hNode, 'input')
        button.receive({})
        const msg = await inputPromise

        // tests
        socket.emit.calledOnce.should.be.true()
        const [eventName, message] = socket.emit.args[0] // [0][0] is the event name, [0][1] is the payload
        eventName.should.equal('msg-input:' + button.id)
        message.should.equal(msg)
        msg.should.have.property('payload').and.be.a.String()
        new Date(msg.payload).getTime().should.be.approximately(Date.now(), 500) // within 0.5 second
    })
    it('should receive a date object in payload', async function () {
        const copyOfFlow = JSON.parse(JSON.stringify(flow))
        const buttonDefinition = copyOfFlow.find(n => n.id === 'node-ui-button')
        buttonDefinition.payload = 'object'
        buttonDefinition.payloadType = 'date'
        await helper.load(nodeImports, copyOfFlow)
        verifyFlowLoaded(helper, copyOfFlow)
        const base = helper.getNode('config-ui-base')

        // setup a fake connection so that button has somewhere to emit to and we can spy on it
        const socket = {
            id: 'fake-conn-id',
            emit: sinon.spy()
        }
        base.uiShared.connections['fake-conn-id'] = socket // fake connection so that button has somewhere to emit

        // send a message to the button node
        const button = helper.getNode('node-ui-button')
        const hNode = helper.getNode('helper-node')
        const inputPromise = on(hNode, 'input')
        button.receive({})
        const msg = await inputPromise

        // tests
        socket.emit.calledOnce.should.be.true()
        const [eventName, message] = socket.emit.args[0] // [0][0] is the event name, [0][1] is the payload
        eventName.should.equal('msg-input:' + button.id)
        message.should.equal(msg)
        msg.should.have.property('payload').and.be.an.instanceOf(Date)
        msg.payload.getTime().should.be.approximately(Date.now(), 500) // within 0.5 second
    })
    it('should receive an epoch time value in payload', async function () {
        const copyOfFlow = JSON.parse(JSON.stringify(flow))
        const buttonDefinition = copyOfFlow.find(n => n.id === 'node-ui-button')
        buttonDefinition.payload = '' // should be blank for epoch time
        buttonDefinition.payloadType = 'date'
        await helper.load(nodeImports, copyOfFlow)
        verifyFlowLoaded(helper, copyOfFlow)
        const base = helper.getNode('config-ui-base')

        // setup a fake connection so that button has somewhere to emit to and we can spy on it
        const socket = {
            id: 'fake-conn-id',
            emit: sinon.spy()
        }
        base.uiShared.connections['fake-conn-id'] = socket // fake connection so that button has somewhere to emit

        // send a message to the button node
        const button = helper.getNode('node-ui-button')
        const hNode = helper.getNode('helper-node')
        const inputPromise = on(hNode, 'input')
        button.receive({})
        const msg = await inputPromise

        // tests
        socket.emit.calledOnce.should.be.true()
        const [eventName, message] = socket.emit.args[0] // [0][0] is the event name, [0][1] is the payload
        eventName.should.equal('msg-input:' + button.id)
        message.should.equal(msg)
        msg.should.have.property('payload').and.be.a.Number()
        msg.payload.should.be.approximately(Date.now(), 500) // within 0.5 second
    })

Comment on lines +61 to +70
switch (payload) {
case 'iso':
payload = new Date().toISOString()
break
case 'object':
payload = new Date()
break
default:
payload = Date.now()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The date should be evaluated by the core node-red typedInput partner function evaluateNodeProperty (to cater for future core changes)

Suggested change
switch (payload) {
case 'iso':
payload = new Date().toISOString()
break
case 'object':
payload = new Date()
break
default:
payload = Date.now()
}
payload = RED.util.evaluateNodeProperty(payload, payloadType, node)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UI-button ignores timestamp payload options

3 participants