diff --git a/CHANGELOG.md b/CHANGELOG.md
index b677aaa..2701e8c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,28 @@
+#### 0.3.4
+
+ - Update dependencies
+
+#### 0.3.3
+
+ - Add plugin stub to runtime (#73) @joepavitt
+ - Use compatible versions rather than specific version of dependencies (#70) @Pezmc
+
+#### 0.3.2
+ 
+ - Fix async module loading (#65) @knolleary
+ - Update README.md (#61) @andreasmarkussen
+
+#### 0.3.1
+ 
+ - Add support for async node modules (#63) @knolleary
+
+#### 0.3.0
+
+ - Require node.js >=14
+ - Add `setFlows` so that node being tested can modify flows (#54) @Steve-Mcl
+
 #### 0.2.7
+
  - Wait for startFlows to resolve before returning from loadFlow call - required with Node-RED 1.3+
  - README.md: Update example unit test to report assertion failures
  - examples: lower-case_spec.js: Allow proper assertion failure reporting (#45)
diff --git a/README.md b/README.md
index 755fd65..d68576d 100644
--- a/README.md
+++ b/README.md
@@ -314,7 +314,44 @@ Loads a flow then starts the flow. This function has the following arguments:
 * testNode: (object|array of objects) Module object of a node to be tested returned by require function. This node will be registered, and can be used in testFlows.
 * testFlow: (array of objects) Flow data to test a node. If you want to use flow data exported from Node-RED editor, export the flow to the clipboard and paste the content into your test scripts.
 * testCredentials: (object) Optional node credentials.
-* cb: (function) Function to call back when testFlows has been started.
+* cb: (function) Function to call back when testFlows has been started  (not required when called wih `await`)
+
+### setFlows(testFlow, type, testCredentials, cb)
+
+Updates the currently loaded flow. This function has the following arguments:
+
+* testFlow: (array of objects) Flow data to test a node. If you want to use flow data exported from Node-RED editor, export the flow to the clipboard and paste the content into your test scripts.
+* type: (string) Flow data to test a node. If you want to use flow data exported from Node-RED editor, export the flow to the clipboard and paste the content into your test scripts.
+* testCredentials: (object) Optional node credentials.
+* cb: (function) Function to call back when testFlows has been loaded (not required when called wih `await`)
+
+#### Example
+
+```js
+  it('should modify the flow then lower case of payload', async function () {
+      const flow = [
+            { id: "n2", type: "helper" }
+      ]
+      await helper.load(lowerNode, flow)
+      const newFlow = [...flow]
+      newFlow.push( { id: "n1", type: "lower-case", name: "lower-case", wires:[['n2']] },)
+      await helper.setFlows(newFlow, "nodes") //update flows
+      const n1 = helper.getNode('n1')
+      n1.should.have.a.property('name', 'lower-case')
+      await new Promise((resolve, reject) => {
+        const n2 = helper.getNode('n2')
+        n2.on('input', function (msg) {
+            try {
+                msg.should.have.property('payload', 'hello');
+                resolve()
+            } catch (err) {
+                reject(err);
+            }
+        });
+        n1.receive({ payload: 'HELLO' });
+      });
+  });
+```
 
 ### unload()
 
@@ -384,8 +421,8 @@ Return the URL of the helper server including the ephemeral port used when start
 Return a spy on the logs to look for events from the node under test.  For example:
 
 ```javascript
-var logEvents = helper.log().args.filter(function(evt {
-    return evt[0].type == "batch";
+var logEvents = helper.log().args.filter(function(event) {
+    return event[0].type == "batch";
 });
 ```
 
diff --git a/examples/lower-case_spec.js b/examples/lower-case_spec.js
index 5e32a30..bc942cf 100644
--- a/examples/lower-case_spec.js
+++ b/examples/lower-case_spec.js
@@ -54,4 +54,28 @@ describe('lower-case Node', function () {
       n1.receive({ payload: "UpperCase" });
     });
   });
+  it('should modify the flow then lower case of payload', async function () {
+      const flow = [
+            { id: "n2", type: "helper" }
+      ]
+      await helper.load(lowerNode, flow)
+      
+      const newFlow = [...flow]
+      newFlow.push( { id: "n1", type: "lower-case", name: "lower-case", wires:[['n2']] },)
+      await helper.setFlows(newFlow)
+      const n1 = helper.getNode('n1')
+      n1.should.have.a.property('name', 'lower-case')
+      await new Promise((resolve, reject) => {
+        const n2 = helper.getNode('n2')
+        n2.on('input', function (msg) {
+            try {
+                msg.should.have.property('payload', 'hello');
+                resolve()
+            } catch (err) {
+                reject(err);
+            }
+        });
+        n1.receive({ payload: 'HELLO' });
+      });
+  });
 });
diff --git a/index.js b/index.js
index c956922..daec3de 100644
--- a/index.js
+++ b/index.js
@@ -16,6 +16,7 @@
 'use strict';
 
 const path = require("path");
+const process = require("process")
 const sinon = require("sinon");
 const should = require('should');
 const fs = require('fs');
@@ -25,17 +26,35 @@ var bodyParser = require("body-parser");
 const express = require("express");
 const http = require('http');
 const stoppable = require('stoppable');
-const readPkgUp = require('read-pkg-up');
 const semver = require('semver');
 const EventEmitter = require('events').EventEmitter;
 
 const PROXY_METHODS = ['log', 'status', 'warn', 'error', 'debug', 'trace', 'send'];
 
+
+// Find the nearest package.json
+function findPackageJson(dir) {
+    dir = path.resolve(dir || process.cwd())
+    const { root } = path.parse(dir)
+    if (dir === root) {
+        return null
+    }
+    const packagePath = path.join(dir, 'package.json')
+    if (fs.existsSync(packagePath)) {
+        return {
+            path: packagePath,
+            packageJson: JSON.parse(fs.readFileSync(packagePath, 'utf-8'))
+        }
+    } else {
+        return findPackageJson(path.resolve(path.join(dir, '..')))
+    }
+}
+
 /**
  * Finds the NR runtime path by inspecting environment
  */
 function findRuntimePath() {
-    const upPkg = readPkgUp.sync();
+    const upPkg = findPackageJson()
     // case 1: we're in NR itself
     if (upPkg.packageJson.name === 'node-red') {
         if (checkSemver(upPkg.packageJson.version,"<0.20.0")) {
@@ -173,7 +192,7 @@ class NodeTestHelper extends EventEmitter {
         return this._settings;
     }
 
-    load(testNode, testFlow, testCredentials, cb) {
+    async load(testNode, testFlow, testCredentials, cb) {
         const log = this._log;
         const logSpy = this._logSpy = this._sandbox.spy(log, 'log');
         logSpy.FATAL = log.FATAL;
@@ -199,17 +218,35 @@ class NodeTestHelper extends EventEmitter {
         });
 
 
-
         if (typeof testCredentials === 'function') {
             cb = testCredentials;
             testCredentials = {};
         }
-
-        var storage = {
+        const conf = {flows:testFlow,credentials:testCredentials|| {}}
+        const storage = {
+            conf: conf,
             getFlows: function () {
-                return Promise.resolve({flows:testFlow,credentials:testCredentials});
+                return Promise.resolve(conf);
+            },
+            saveFlows: function(conf) {
+                storage.conf = conf;
+                return Promise.resolve();
             }
         };
+
+        // mock out the runtime plugins api
+        const plugins = {
+            registerPlugin () {
+                return;
+            },
+            getPlugin () {
+                return;
+            },
+            getPluginsByType () {
+                return [];
+            }
+        }
+
         // this._settings.logging = {console:{level:'off'}};
         this._settings.available = function() { return false; }
 
@@ -224,13 +261,13 @@ class NodeTestHelper extends EventEmitter {
             util: this._RED.util,
             settings: this._settings,
             storage: storage,
+            plugins: plugins,
             log: this._log,
             nodeApp: express(),
             adminApp: this._httpAdmin,
             library: {register: function() {}},
             get server() { return self._server }
         }
-
         redNodes.init(mockRuntime);
         redNodes.registerType("helper", function (n) {
             redNodes.createNode(this, n);
@@ -252,12 +289,16 @@ class NodeTestHelper extends EventEmitter {
                     Object.defineProperty(red, prop, propDescriptor);
                 });
         }
+        const initPromises = []
 
         let preloadedCoreModules = new Set();
         testFlow.forEach(n => {
             if (this._nodeModules.hasOwnProperty(n.type)) {
                 // Go find the 'real' core node module and load it...
-                this._nodeModules[n.type](red);
+                const result = this._nodeModules[n.type](red);
+                if (result?.then) {
+                    initPromises.push(result)
+                }
                 preloadedCoreModules.add(this._nodeModules[n.type]);
             }
         })
@@ -267,26 +308,33 @@ class NodeTestHelper extends EventEmitter {
         }
         testNode.forEach(fn => {
             if (!preloadedCoreModules.has(fn)) {
-                fn(red);
+                const result = fn(red);
+                if (result?.then) {
+                    initPromises.push(result)
+                }
             }
         });
-
-        return redNodes.loadFlows()
-            .then(redNodes.startFlows).then(() => {
-                should.deepEqual(testFlow, redNodes.getFlows().flows);
-                if(cb) cb();
-            });
+        try {
+            await Promise.all(initPromises);
+            await redNodes.loadFlows();
+            await redNodes.startFlows();
+            should.deepEqual(testFlow, redNodes.getFlows().flows);
+            if (cb) cb();
+        } catch (error) {
+            if (cb) cb(error);
+            else throw error;
+        }
     }
 
     unload() {
         // TODO: any other state to remove between tests?
         this._redNodes.clearRegistry();
-        this._logSpy.restore();
+        this._logSpy && this._logSpy.restore();
         this._sandbox.restore();
 
         // internal API
         this._context.clean({allNodes:[]});
-        return this._redNodes.stopFlows();
+        return this._redNodes.stopFlows()
     }
 
     /**
@@ -302,41 +350,106 @@ class NodeTestHelper extends EventEmitter {
         return this._redNodes.stopFlows();
     }
 
+    /**
+     * Update flows
+     * @param {object|object[]} testFlow Flow data to test a node
+     * @param {"full"|"flows"|"nodes"} type The type of deploy mode "full", "flows" or "nodes" (defaults to "full") 
+     * @param {object} [testCredentials] Optional node credentials
+     * @param {function} [cb] Optional callback (not required when called with await)
+     * @returns {Promise}
+     */
+    async setFlows(testFlow, type, testCredentials, cb) {
+        const helper = this;
+        if (typeof testCredentials === 'string' ) {
+            cb = testCredentials;
+            testCredentials = {};
+        }
+        if(!type || typeof type != "string") {
+            type = "full"
+        }
+        async function waitStarted() {
+            return new Promise((resolve, reject) => {
+                let timeover = setTimeout(() => {
+                    if (timeover) {
+                        timeover = null
+                        reject(Error("timeout waiting event"))
+                    }
+                }, 300);
+                function hander() {
+                    clearTimeout(timeover)
+                    helper._events.off('flows:started', hander)
+                    if (timeover) {
+                        timeover = null
+                        resolve()
+                    }
+                }
+                helper._events.on('flows:started', hander); // call resolve when its done
+            });
+        }
+        try {
+            await this._redNodes.setFlows(testFlow, testCredentials || {}, type);
+            await waitStarted();
+            
+            if (cb) cb();
+        } catch (error) {
+            if (cb) cb(error);
+            else throw error;
+        }
+    }
+
     request() {
         return request(this._httpAdmin);
     }
 
-    startServer(done) {
-        this._app = express();
-        const server = stoppable(http.createServer((req, res) => {
-            this._app(req, res);
-        }), 0);
-
-        this._RED.init(server,{
-            logging:{console:{level:'off'}}
-        });
-        server.listen(this._listenPort, this._address);
-        server.on('listening', () => {
-            this._port = server.address().port;
-            // internal API
-            this._comms.start();
-            done();
-        });
-        this._server = server;
+    async startServer(cb) {
+        try {
+            await new Promise((resolve, reject) => {
+                this._app = express();
+                const server = stoppable(
+                    http.createServer((req, res) => this._app(req, res)),
+                    0
+                );
+    
+                this._RED.init(server, {
+                    logging: { console: { level: 'off' } },
+                });
+    
+                server.listen(this._listenPort, this._address);
+    
+                server.on('listening', () => {
+                    this._port = server.address().port;
+                    this._comms.start();
+                    this._server = server;
+                    resolve();
+                });
+    
+                server.on('error', reject);
+            });
+    
+            if (cb) cb();
+        } catch (error) {
+            if (cb) cb(error);
+            else throw error;
+        }
     }
-
-    //TODO consider saving TCP handshake/server reinit on start/stop/start sequences
-    stopServer(done) {
-        if (this._server) {
-            try {
-                // internal API
-                this._comms.stop();
-                this._server.stop(done);
-            } catch (e) {
-                done();
+    
+    async stopServer(cb) {
+        try {
+            if (this._server) {
+                await new Promise((resolve, reject) => {
+                    this._comms.stop();
+    
+                    this._server.stop((error) => {
+                        if (error) reject(error);
+                        else resolve();
+                    });
+                });
             }
-        } else {
-            done();
+    
+            if (cb) cb();
+        } catch (error) {
+            if (cb) cb(error);
+            else throw error;
         }
     }
 
diff --git a/package.json b/package.json
index db9ebbf..81a71d9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "node-red-node-test-helper",
-  "version": "0.2.7",
+  "version": "0.3.4",
   "description": "A test framework for Node-RED nodes",
   "main": "index.js",
   "scripts": {
@@ -13,18 +13,17 @@
     "url": "https://github.com/node-red/node-red-node-test-helper.git"
   },
   "dependencies": {
-    "express": "4.17.1",
-    "body-parser": "1.19.0",
-    "read-pkg-up": "7.0.1",
-    "semver": "7.3.4",
+    "body-parser": "^1.20.3",
+    "express": "^4.21.0",
+    "semver": "^7.5.4",
     "should": "^13.2.3",
-    "should-sinon": "0.0.6",
-    "sinon": "9.2.4",
-    "stoppable": "1.1.0",
-    "supertest": "4.0.2"
+    "should-sinon": "^0.0.6",
+    "sinon": "^11.1.2",
+    "stoppable": "^1.1.0",
+    "supertest": "^7.0.0"
   },
   "devDependencies": {
-    "mocha": "~7.1.2"
+    "mocha": "^9.2.2"
   },
   "contributors": [
     {
@@ -46,6 +45,6 @@
     "node-red"
   ],
   "engines": {
-    "node": ">=8"
+    "node": ">=14"
   }
 }