mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
f7210effec
Allows for safe removal of hooks whilst they are being invoked
339 lines
11 KiB
JavaScript
339 lines
11 KiB
JavaScript
const should = require("should");
|
|
const NR_TEST_UTILS = require("nr-test-utils");
|
|
|
|
const hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks");
|
|
|
|
describe("util/hooks", function() {
|
|
afterEach(function() {
|
|
hooks.clear();
|
|
})
|
|
it("allows a hook to be registered", function(done) {
|
|
let calledWith = null;
|
|
hooks.has("onSend").should.be.false();
|
|
hooks.add("onSend", function(payload) { calledWith = payload } )
|
|
hooks.has("onSend").should.be.true();
|
|
let data = { a: 1 };
|
|
hooks.trigger("onSend",data,err => {
|
|
calledWith.should.equal(data);
|
|
done(err);
|
|
})
|
|
})
|
|
it("rejects invalid hook id", function(done) {
|
|
try {
|
|
hooks.add("foo", function(payload) {})
|
|
done(new Error("Invalid hook accepted"))
|
|
} catch(err) {
|
|
done();
|
|
}
|
|
})
|
|
it("calls hooks in the order they were registered", function(done) {
|
|
hooks.add("onSend", function(payload) { payload.order.push("A") } )
|
|
hooks.add("onSend", function(payload) { payload.order.push("B") } )
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["A","B"])
|
|
done(err);
|
|
})
|
|
})
|
|
|
|
it("does not allow multiple hooks with same id.label", function() {
|
|
hooks.has("onSend.one").should.be.false();
|
|
hooks.has("onSend").should.be.false();
|
|
hooks.add("onSend.one", function(payload) { payload.order.push("A") } );
|
|
hooks.has("onSend.one").should.be.true();
|
|
hooks.has("onSend").should.be.true();
|
|
(function() {
|
|
hooks.add("onSend.one", function(payload) { payload.order.push("B") } )
|
|
}).should.throw();
|
|
})
|
|
|
|
it("removes labelled hook", function(done) {
|
|
hooks.has("onSend.A").should.be.false();
|
|
hooks.has("onSend.B").should.be.false();
|
|
hooks.has("onSend").should.be.false();
|
|
|
|
hooks.add("onSend.A", function(payload) { payload.order.push("A") } )
|
|
|
|
hooks.has("onSend.A").should.be.true();
|
|
hooks.has("onSend.B").should.be.false();
|
|
hooks.has("onSend").should.be.true();
|
|
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
hooks.has("onSend.A").should.be.true();
|
|
hooks.has("onSend.B").should.be.true();
|
|
hooks.has("onSend").should.be.true();
|
|
|
|
hooks.remove("onSend.A");
|
|
|
|
hooks.has("onSend.A").should.be.false();
|
|
hooks.has("onSend.B").should.be.true();
|
|
hooks.has("onSend").should.be.true();
|
|
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
try {
|
|
data.order.should.eql(["B"])
|
|
|
|
hooks.remove("onSend.B");
|
|
|
|
hooks.has("onSend.A").should.be.false();
|
|
hooks.has("onSend.B").should.be.false();
|
|
hooks.has("onSend").should.be.false();
|
|
|
|
done(err);
|
|
} catch(err2) {
|
|
done(err2);
|
|
}
|
|
})
|
|
})
|
|
|
|
it("cannot remove unlabelled hook", function() {
|
|
hooks.add("onSend", function(payload) { payload.order.push("A") } );
|
|
(function() {
|
|
hooks.remove("onSend")
|
|
}).should.throw();
|
|
})
|
|
it("removes all hooks with same label", function(done) {
|
|
hooks.add("onSend.A", function(payload) { payload.order.push("A") } )
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
hooks.add("preRoute.A", function(payload) { payload.order.push("C") } )
|
|
hooks.add("preRoute.B", function(payload) { payload.order.push("D") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["A","B"])
|
|
hooks.trigger("preRoute", data, err => {
|
|
data.order.should.eql(["A","B","C","D"])
|
|
|
|
data.order = [];
|
|
|
|
hooks.remove("*.A");
|
|
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["B"])
|
|
hooks.trigger("preRoute", data, err => {
|
|
data.order.should.eql(["B","D"])
|
|
})
|
|
done(err);
|
|
})
|
|
})
|
|
})
|
|
})
|
|
it("allows a hook to remove itself whilst being called", function(done) {
|
|
let data = { order: [] }
|
|
hooks.add("onSend.A", function(payload) { payload.order.push("A") } )
|
|
hooks.add("onSend.B", function(payload) {
|
|
hooks.remove("*.B");
|
|
})
|
|
hooks.add("onSend.C", function(payload) { payload.order.push("C") } )
|
|
hooks.add("onSend.D", function(payload) { payload.order.push("D") } )
|
|
|
|
hooks.trigger("onSend", data, err => {
|
|
try {
|
|
should.not.exist(err);
|
|
data.order.should.eql(["A","C","D"])
|
|
done();
|
|
} catch(e) {
|
|
done(e);
|
|
}
|
|
})
|
|
});
|
|
|
|
it("allows a hook to remove itself and others whilst being called", function(done) {
|
|
let data = { order: [] }
|
|
hooks.add("onSend.A", function(payload) { payload.order.push("A") } )
|
|
hooks.add("onSend.B", function(payload) {
|
|
hooks.remove("*.B");
|
|
hooks.remove("*.C");
|
|
})
|
|
hooks.add("onSend.C", function(payload) { payload.order.push("C") } )
|
|
hooks.add("onSend.D", function(payload) { payload.order.push("D") } )
|
|
|
|
hooks.trigger("onSend", data, err => {
|
|
try {
|
|
should.not.exist(err);
|
|
data.order.should.eql(["A","D"])
|
|
done();
|
|
} catch(e) {
|
|
done(e);
|
|
}
|
|
})
|
|
});
|
|
|
|
it("halts execution on return false", function(done) {
|
|
hooks.add("onSend.A", function(payload) { payload.order.push("A"); return false } )
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["A"])
|
|
err.should.be.false();
|
|
done();
|
|
})
|
|
})
|
|
it("halts execution on thrown error", function(done) {
|
|
hooks.add("onSend.A", function(payload) { payload.order.push("A"); throw new Error("error") } )
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["A"])
|
|
should.exist(err);
|
|
err.should.not.be.false()
|
|
done();
|
|
})
|
|
})
|
|
|
|
it("handler can use callback function", function(done) {
|
|
hooks.add("onSend.A", function(payload, done) {
|
|
setTimeout(function() {
|
|
payload.order.push("A")
|
|
done()
|
|
},30)
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["A","B"])
|
|
done(err);
|
|
})
|
|
})
|
|
|
|
it("handler can use callback function - halt execution", function(done) {
|
|
hooks.add("onSend.A", function(payload, done) {
|
|
setTimeout(function() {
|
|
payload.order.push("A")
|
|
done(false)
|
|
},30)
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["A"])
|
|
err.should.be.false()
|
|
done();
|
|
})
|
|
})
|
|
it("handler can use callback function - halt on error", function(done) {
|
|
hooks.add("onSend.A", function(payload, done) {
|
|
setTimeout(function() {
|
|
done(new Error("test error"))
|
|
},30)
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql([])
|
|
should.exist(err);
|
|
err.should.not.be.false()
|
|
done();
|
|
})
|
|
})
|
|
|
|
it("handler be an async function", function(done) {
|
|
hooks.add("onSend.A", async function(payload) {
|
|
return new Promise(resolve => {
|
|
setTimeout(function() {
|
|
payload.order.push("A")
|
|
resolve()
|
|
},30)
|
|
});
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["A","B"])
|
|
done(err);
|
|
})
|
|
})
|
|
|
|
it("handler be an async function - halt execution", function(done) {
|
|
hooks.add("onSend.A", async function(payload) {
|
|
return new Promise(resolve => {
|
|
setTimeout(function() {
|
|
payload.order.push("A")
|
|
resolve(false)
|
|
},30)
|
|
});
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql(["A"])
|
|
done(err);
|
|
})
|
|
})
|
|
it("handler be an async function - halt on error", function(done) {
|
|
hooks.add("onSend.A", async function(payload) {
|
|
return new Promise((resolve,reject) => {
|
|
setTimeout(function() {
|
|
reject(new Error("test error"))
|
|
},30)
|
|
});
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data,err => {
|
|
data.order.should.eql([])
|
|
should.exist(err);
|
|
err.should.not.be.false()
|
|
done();
|
|
})
|
|
})
|
|
|
|
|
|
it("handler can use callback function - promise API", function(done) {
|
|
hooks.add("onSend.A", function(payload, done) {
|
|
setTimeout(function() {
|
|
payload.order.push("A")
|
|
done()
|
|
},30)
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data).then(() => {
|
|
data.order.should.eql(["A","B"])
|
|
done()
|
|
}).catch(done)
|
|
})
|
|
|
|
it("handler can halt execution - promise API", function(done) {
|
|
hooks.add("onSend.A", function(payload, done) {
|
|
setTimeout(function() {
|
|
payload.order.push("A")
|
|
done(false)
|
|
},30)
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data).then(() => {
|
|
data.order.should.eql(["A"])
|
|
done()
|
|
}).catch(done)
|
|
})
|
|
|
|
it("handler can halt execution on error - promise API", function(done) {
|
|
hooks.add("onSend.A", function(payload, done) {
|
|
throw new Error("error");
|
|
})
|
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
|
|
|
let data = { order:[] };
|
|
hooks.trigger("onSend",data).then(() => {
|
|
done("hooks.trigger resolved unexpectedly")
|
|
}).catch(err => {
|
|
done();
|
|
})
|
|
})
|
|
});
|