rooibos-roku 5.16.3 → 5.16.5

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/CHANGELOG.md CHANGED
@@ -6,6 +6,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
 
8
8
 
9
+ ## [5.16.5](https://github.com/rokucommunity/rooibos/compare/5.16.4...v5.16.5) - 2026-09-09
10
+ ### Changed
11
+ - Security enhancements ([#405](https://github.com/rokucommunity/rooibos/pull/405), [#406](https://github.com/rokucommunity/rooibos/pull/406), [#407](https://github.com/rokucommunity/rooibos/pull/407), [#409](https://github.com/rokucommunity/rooibos/pull/409), [#410](https://github.com/rokucommunity/rooibos/pull/410), [#411](https://github.com/rokucommunity/rooibos/pull/411), [#412](https://github.com/rokucommunity/rooibos/pull/412), [#414](https://github.com/rokucommunity/rooibos/pull/414), [#421](https://github.com/rokucommunity/rooibos/pull/421))
12
+ - Make before-exit sleep duration configurable ([#418](https://github.com/rokucommunity/rooibos/pull/418))
13
+ - Run stubCall/expectCalled/expectNotCalled rewrites in setUp/tearDown/beforeEach/afterEach hooks ([#403](https://github.com/rokucommunity/rooibos/pull/403))
14
+ - upgrade to [brighterscript@0.73.3](https://github.com/rokucommunity/brighterscript/blob/master/CHANGELOG.md#0733---2026-09-09). Notable changes since 0.72.5:
15
+ - Security enhancements ([#1733](https://github.com/rokucommunity/brighterscript/pull/1733), [#1763](https://github.com/rokucommunity/brighterscript/pull/1763), [#1764](https://github.com/rokucommunity/brighterscript/pull/1764), [#1766](https://github.com/rokucommunity/brighterscript/pull/1766), [#1773](https://github.com/rokucommunity/brighterscript/pull/1773), [#1774](https://github.com/rokucommunity/brighterscript/pull/1774), [#1775](https://github.com/rokucommunity/brighterscript/pull/1775), [#1782](https://github.com/rokucommunity/brighterscript/pull/1782), [#1796](https://github.com/rokucommunity/brighterscript/pull/1796))
16
+ - upgrade to [roku-debug@0.24.3](https://github.com/rokucommunity/roku-debug/blob/master/CHANGELOG.md#0243---2026-09-09). Notable changes since 0.23.11:
17
+ - Security enhancements ([#369](https://github.com/rokucommunity/roku-debug/pull/369), [#372](https://github.com/rokucommunity/roku-debug/pull/372), [#387](https://github.com/rokucommunity/roku-debug/pull/387), [#402](https://github.com/rokucommunity/roku-debug/pull/402), [#403](https://github.com/rokucommunity/roku-debug/pull/403), [#407](https://github.com/rokucommunity/roku-debug/pull/407), [#412](https://github.com/rokucommunity/roku-debug/pull/412), [#414](https://github.com/rokucommunity/roku-debug/pull/414), [#417](https://github.com/rokucommunity/roku-debug/pull/417))
18
+ - upgrade to [roku-deploy@3.18.4](https://github.com/rokucommunity/roku-deploy/blob/master/CHANGELOG.md#3184---2026-09-02). Notable changes since 3.17.6:
19
+ - Security enhancements ([#293](https://github.com/rokucommunity/roku-deploy/pull/293), [#345](https://github.com/rokucommunity/roku-deploy/pull/345))
20
+ - upgrade to [@rokucommunity/bslint@0.8.46](https://github.com/rokucommunity/bslint/blob/master/CHANGELOG.md#0846---2026-09-09). Notable changes since 0.8.43:
21
+ - Security enhancements ([#186](https://github.com/rokucommunity/bslint/pull/186), [#196](https://github.com/rokucommunity/bslint/pull/196), [#198](https://github.com/rokucommunity/bslint/pull/198), [#203](https://github.com/rokucommunity/bslint/pull/203))
22
+ - upgrade to [ropm@0.11.11](https://github.com/rokucommunity/ropm/blob/master/CHANGELOG.md#01111---2026-09-09). Notable changes since 0.11.8:
23
+ - Security enhancements ([#147](https://github.com/rokucommunity/ropm/pull/147), [#152](https://github.com/rokucommunity/ropm/pull/152), [#154](https://github.com/rokucommunity/ropm/pull/154), [#156](https://github.com/rokucommunity/ropm/pull/156), [#159](https://github.com/rokucommunity/ropm/pull/159))
24
+ ### Fixed
25
+ - Fix coverage instrumentation changing constructor execution order ([#355](https://github.com/rokucommunity/rooibos/pull/355))
26
+ - fix: skip assertion tracking injection for namespace function calls ([#385](https://github.com/rokucommunity/rooibos/pull/385))
27
+
28
+
29
+
30
+ ## [5.16.4](https://github.com/rokucommunity/rooibos/compare/5.16.3...v5.16.4) - 2026-06-10
31
+ ### Changed
32
+ - Make tests less brittle - removes full transpile string checks ([#396](https://github.com/rokucommunity/rooibos/pull/396))
33
+ - Security enhancements
34
+ - upgrade to [brighterscript@0.72.5](https://github.com/rokucommunity/brighterscript/blob/master/CHANGELOG.md#0725---2026-06-10). Notable changes since 0.72.2:
35
+ - Security enhancements
36
+ - upgrade to [roku-debug@0.23.11](https://github.com/rokucommunity/roku-debug/blob/master/CHANGELOG.md#02311---2026-06-04). Notable changes since 0.23.8:
37
+ - Fix pressHomeButton crash in disconnectRequest ([#358](https://github.com/rokucommunity/roku-debug/pull/358))
38
+ - Security enhancements
39
+ - upgrade to [roku-deploy@3.17.6](https://github.com/rokucommunity/roku-deploy/blob/master/CHANGELOG.md#3176---2026-06-04). Notable changes since 3.17.4:
40
+ - Fix case-insensitive matching for absolute `files.src` glob patterns on case-insensitive file systems ([#279](https://github.com/rokucommunity/roku-deploy/pull/279))
41
+ - Preserve `!` glob-negation prefix in standardizePath ([#277](https://github.com/rokucommunity/roku-deploy/pull/277))
42
+ - Security enhancements
43
+ - upgrade to [ropm@0.11.8](https://github.com/rokucommunity/ropm/blob/master/CHANGELOG.md#0118---2026-05-30). Notable changes since 0.11.7:
44
+ - Security enhancements
45
+
46
+
47
+
9
48
  ## [5.16.3](https://github.com/rokucommunity/rooibos/compare/5.16.2...v5.16.3) - 2026-05-20
10
49
  ### Changed
11
50
  - Update ropm to 0.11.7 and remove lodash override ([#391](https://github.com/rokucommunity/rooibos/pull/391))
package/README.md CHANGED
@@ -30,6 +30,16 @@ Rooibos is an independent open-source project maintained exclusively by voluntee
30
30
 
31
31
  You might want to help! Get in touch via the [slack group](https://join.slack.com/t/rokudevelopers/shared_invite/zt-4vw7rg6v-NH46oY7hTktpRIBM_zGvwA) or [raise issues](https://github.com/rokucommunity/rooibos/issues/new).
32
32
 
33
+ To work on Rooibos locally:
34
+
35
+ ```bash
36
+ npm install
37
+ npx ropm copy # copies the framework's ropm dependencies into framework/src
38
+ npm test
39
+ ```
40
+
41
+ `npx ropm copy` is required because `npm install` alone doesn't copy the ropm modules the framework depends on. `npm run build` also runs it for you, and the test/device scripts will tell you if you've forgotten it.
42
+
33
43
  ## Sample project
34
44
 
35
45
  https://github.com/rokucommunity/rooibos-roku-sample
@@ -47,3 +57,7 @@ Yes, it's been used in production by quite a few respectable companies. There ar
47
57
  ### Is Rooibos actively maintained?
48
58
 
49
59
  We at RokuCommunity love rooibos, and regularly keep it up to date.
60
+
61
+ ## Accepted security advisories
62
+
63
+ Dependencies flagged by `npm audit` that we have reviewed and chosen not to upgrade are tracked in [audit-ci.jsonc](https://github.com/RokuCommunity/rooibos/blob/master/audit-ci.jsonc). Each entry includes the advisory ID, the date it was added, and the reason it does not apply to this project.
package/audit-ci.jsonc ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ // Fail on high severity and above. See README "Accepted security advisories" for
3
+ // why each allowlisted entry is safe to defer.
4
+ "$schema": "https://github.com/IBM/audit-ci/raw/main/docs/schema.json",
5
+ "high": true,
6
+ "allowlist": [
7
+ {
8
+ "GHSA-w5hq-g745-h8pq": {
9
+ // Added: 2026-05-22
10
+ "active": true,
11
+ "notes": "`uuid` <11.1.1 (via `postman-request` and dev-only `nyc>istanbul-lib-processinfo`): vulnerable code path is `v3()`/`v5()`/`v6()` when a caller-provided buffer is passed. All consumers in this dep graph call only `v4()` (no buffer arg), so the vulnerable path is unreachable."
12
+ }
13
+ }
14
+ ]
15
+ }
package/dist/cli.js CHANGED
@@ -6,6 +6,33 @@ const brighterscript_1 = require("brighterscript");
6
6
  const yargs = require("yargs");
7
7
  const roku_deploy_1 = require("roku-deploy");
8
8
  const fs = require("fs");
9
+ const path = require("path");
10
+ /**
11
+ * Load simple `KEY=value` pairs from a .env file into process.env, without
12
+ * overwriting variables that are already set in the real environment.
13
+ */
14
+ function loadDotEnv(envPath = '.env') {
15
+ var _a;
16
+ var _b, _c;
17
+ if (!fs.existsSync(envPath)) {
18
+ return;
19
+ }
20
+ const contents = fs.readFileSync(envPath, 'utf8');
21
+ for (const line of contents.split(/\r?\n/)) {
22
+ const match = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
23
+ if (!match) {
24
+ //skip blanks and comments
25
+ continue;
26
+ }
27
+ let value = match[2].trim();
28
+ //strip matching surrounding quotes
29
+ if (/^"(.*)"$/.test(value) || /^'(.*)'$/.test(value)) {
30
+ value = value.slice(1, -1);
31
+ }
32
+ (_a = (_b = process.env)[_c = match[1]]) !== null && _a !== void 0 ? _a : (_b[_c] = value);
33
+ }
34
+ }
35
+ loadDotEnv();
9
36
  let options = yargs
10
37
  .usage('$0', 'Rooibos: a simple, flexible, fun Brightscript test framework for Roku Scenegraph apps')
11
38
  .help('help', 'View help information about this tool.')
@@ -15,11 +42,11 @@ let options = yargs
15
42
  .option('log-level', { type: 'string', defaultDescription: '"log"', description: 'The log level. Value can be "error", "warn", "log", "info", "debug".' })
16
43
  .check((argv) => {
17
44
  var _a;
18
- if (!argv.host) {
19
- return new Error('You must provide a host. (--host)');
45
+ if (!argv.host && !process.env.ROKU_HOST) {
46
+ return new Error('You must provide a host. (--host, or ROKU_HOST in .env)');
20
47
  }
21
- if (!argv.password) {
22
- return new Error('You must provide a password. (--password)');
48
+ if (!argv.password && !process.env.ROKU_PASSWORD) {
49
+ return new Error('You must provide a password. (--password, or ROKU_PASSWORD in .env)');
23
50
  }
24
51
  if (!argv.project) {
25
52
  console.log('No project file specified. Using "./bsconfig.json"');
@@ -32,22 +59,23 @@ let options = yargs
32
59
  })
33
60
  .argv;
34
61
  async function main() {
35
- var _a, _b, _c, _d;
62
+ var _a, _b, _c, _d, _e, _f;
36
63
  let currentErrorCode = 0;
37
64
  let bsconfigPath = (_a = options.project) !== null && _a !== void 0 ? _a : 'bsconfig.json';
38
65
  console.log(`Using bsconfig: ${bsconfigPath}`);
39
66
  const rawConfig = brighterscript_1.util.loadConfigFile(bsconfigPath);
40
67
  const bsConfig = brighterscript_1.util.normalizeConfig(rawConfig);
41
- const host = (_b = options.host) !== null && _b !== void 0 ? _b : bsConfig.host;
42
- const password = (_c = options.password) !== null && _c !== void 0 ? _c : bsConfig.password;
43
- const logLevel = (_d = brighterscript_1.LogLevel[options['log-level']]) !== null && _d !== void 0 ? _d : bsConfig.logLevel;
68
+ const host = (_c = (_b = options.host) !== null && _b !== void 0 ? _b : bsConfig.host) !== null && _c !== void 0 ? _c : process.env.ROKU_HOST;
69
+ const password = (_e = (_d = options.password) !== null && _d !== void 0 ? _d : bsConfig.password) !== null && _e !== void 0 ? _e : process.env.ROKU_PASSWORD;
70
+ const logLevel = (_f = brighterscript_1.LogLevel[options['log-level']]) !== null && _f !== void 0 ? _f : bsConfig.logLevel;
44
71
  const builder = new brighterscript_1.ProgramBuilder();
45
72
  builder.logger.logLevel = logLevel;
46
73
  await builder.run(Object.assign(Object.assign({}, options), { retainStagingDir: true, createPackage: true }));
74
+ const device = { host: host };
47
75
  const rokuDeploy = new roku_deploy_1.RokuDeploy();
48
- const deviceInfo = await rokuDeploy.getDeviceInfo({ host: host });
49
- const rendezvousTracker = new roku_debug_1.RendezvousTracker({ softwareVersion: deviceInfo['software-version'] }, { host: host, remotePort: 8085 });
50
- const telnet = new roku_debug_1.TelnetAdapter({ host: options.host }, rendezvousTracker);
76
+ const deviceInfo = await rokuDeploy.getDeviceInfo({ device: device });
77
+ const rendezvousTracker = new roku_debug_1.RendezvousTracker({ softwareVersion: deviceInfo['software-version'] }, { device: device, remotePort: 8085 });
78
+ const telnet = new roku_debug_1.TelnetAdapter({ device: device }, rendezvousTracker);
51
79
  telnet.logger.logLevel = logLevel;
52
80
  await telnet.activate();
53
81
  await telnet.connect();
@@ -57,7 +85,7 @@ async function main() {
57
85
  if (emitAppExit) {
58
86
  telnet.beginAppExit();
59
87
  }
60
- await rokuDeploy.pressHomeButton(host); // roku-deploy v4: keyPress({ host: options.host, key: 'home' });
88
+ await rokuDeploy.keyPress({ device: device, key: 'Home' });
61
89
  process.exit(currentErrorCode);
62
90
  }
63
91
  telnet.on('console-output', (output) => {
@@ -96,11 +124,10 @@ async function main() {
96
124
  async function deployBuiltFiles() {
97
125
  const outFile = bsConfig.outFile;
98
126
  console.log(`Deploying ${outFile} to ${host}`);
99
- await rokuDeploy.publish({
127
+ await rokuDeploy.sideload({
100
128
  password: password,
101
- host: host,
102
- outFile: outFile,
103
- outDir: process.cwd()
129
+ device: device,
130
+ zip: path.resolve(process.cwd(), outFile)
104
131
  });
105
132
  }
106
133
  await deployBuiltFiles();
@@ -685,7 +685,7 @@ sub rooibos_promises_internal_delay(callback as Function, context as dynamic, du
685
685
  end sub
686
686
 
687
687
  ' Observes a node field using observeFieldScoped
688
- ' @param {roSGNode} node - The node to apply the observer
688
+ ' @param {rooibos.promises.roSGNode} node - The node to apply the observer
689
689
  ' @param {String} field - The name of the field to be monitored.
690
690
  ' @param {Dynamic} callback - The name or message port to be executed when the value of the field changes.
691
691
  ' @return true if field could be observed, false if not
@@ -704,7 +704,7 @@ function rooibos_promises_internal_observeFieldScoped(node as object, field as s
704
704
  end function
705
705
 
706
706
  ' Unobserve a node field using unobserveFieldScoped
707
- ' @param {roSGNode} node - The node to remove the observer from
707
+ ' @param {rooibos.promises.roSGNode} node - The node to remove the observer from
708
708
  ' @param {String} field - The name of the field to no longer be monitored.
709
709
  ' @return true if field could be unobserved, false if not
710
710
  function rooibos_promises_internal_unobserveFieldScoped(node as object, field as string)
@@ -1,49 +1,49 @@
1
+ namespace rooibos.promises
1
2
  ' @rokucommunity/promises v0.5.0
2
3
  ' Create a new promise
3
- namespace rooibos.promises
4
- function create() as dynamic
4
+ function create() as dynamic
5
5
  end function
6
6
  end namespace
7
- ' TODO rename this to `then` once BrighterScript supports using keywords as namespaced function names
8
7
  namespace rooibos.promises
9
- function onThen(promise as dynamic, callback as function, context = "__INVALID__" as object) as dynamic
8
+ ' TODO rename this to `then` once BrighterScript supports using keywords as namespaced function names
9
+ function onThen(promise as dynamic, callback as function, context = "__INVALID__" as object) as dynamic
10
10
  end function
11
11
  end namespace
12
- ' TODO rename this to `catch` once BrighterScript supports using keywords as namespaced function names
13
12
  namespace rooibos.promises
14
- function onCatch(promise as dynamic, callback as function, context = "__INVALID__" as object) as dynamic
13
+ ' TODO rename this to `catch` once BrighterScript supports using keywords as namespaced function names
14
+ function onCatch(promise as dynamic, callback as function, context = "__INVALID__" as object) as dynamic
15
15
  end function
16
16
  end namespace
17
- ' TODO rename this to `finally` once BrighterScript supports using keywords as namespaced function names
18
17
  namespace rooibos.promises
19
- function onFinally(promise as dynamic, callback as function, context = "__INVALID__" as object) as dynamic
18
+ ' TODO rename this to `finally` once BrighterScript supports using keywords as namespaced function names
19
+ function onFinally(promise as dynamic, callback as function, context = "__INVALID__" as object) as dynamic
20
20
  end function
21
21
  end namespace
22
- ' Takes an array of promises as input and returns a single Promise.
22
+ namespace rooibos.promises
23
+ ' Takes an array of promises as input and returns a single Promise.
23
24
  ' This returned promise fulfills when all of the input's promises fulfill (including when an empty array is passed), with an array of the fulfillment values.
24
25
  ' It rejects when any of the input's promises rejects, with this first rejection reason.
25
- namespace rooibos.promises
26
- function all(promiseArray as dynamic) as dynamic
26
+ function all(promiseArray as dynamic) as dynamic
27
27
  end function
28
28
  end namespace
29
- ' Takes an array of promises as input and returns a single Promise.
29
+ namespace rooibos.promises
30
+ ' Takes an array of promises as input and returns a single Promise.
30
31
  ' This returned promise fulfills when all of the input's promises settle (including when an empty array is passed),
31
32
  ' with an array of objects that describe the outcome of each promise.
32
- namespace rooibos.promises
33
- function allSettled(promiseArray as dynamic) as dynamic
33
+ function allSettled(promiseArray as dynamic) as dynamic
34
34
  end function
35
35
  end namespace
36
- ' Takes an array of promises as input and returns a single Promise.
36
+ namespace rooibos.promises
37
+ ' Takes an array of promises as input and returns a single Promise.
37
38
  ' This returned promise fulfills when any of the input's promises fulfills, with this first fulfillment value.
38
39
  ' It rejects when all of the input's promises reject (including when an empty array is passed), with an AggregateError containing an array of rejection reasons.
39
- namespace rooibos.promises
40
- function any(promiseArray as dynamic) as dynamic
40
+ function any(promiseArray as dynamic) as dynamic
41
41
  end function
42
42
  end namespace
43
- ' Takes an array of promises as input and returns a single Promise.
44
- ' This returned promise settles with the eventual state of the first promise that settles.
45
43
  namespace rooibos.promises
46
- function race(promiseArray as dynamic) as dynamic
44
+ ' Takes an array of promises as input and returns a single Promise.
45
+ ' This returned promise settles with the eventual state of the first promise that settles.
46
+ function race(promiseArray as dynamic) as dynamic
47
47
  end function
48
48
  end namespace
49
49
  namespace rooibos.promises
@@ -58,29 +58,29 @@ end namespace
58
58
  function isComplete(promise as object) as boolean
59
59
  end function
60
60
  end namespace
61
- ' Determines if the given item is a promise.
61
+ namespace rooibos.promises
62
+ ' Determines if the given item is a promise.
62
63
  '
63
64
  ' Will return true if at least one of the following conditions are true:
64
65
  ' - the SubType exactly equals "Promise"
65
66
  ' - the subtype ends with "_promise" case insensitive
66
67
  ' - the node has a field called "promiseState"
67
- namespace rooibos.promises
68
- function isPromise(promise as dynamic) as boolean
68
+ function isPromise(promise as dynamic) as boolean
69
69
  end function
70
70
  end namespace
71
- ' Remove all promise storage from the current m
72
71
  namespace rooibos.promises
73
- sub clean()
72
+ ' Remove all promise storage from the current m
73
+ sub clean()
74
74
  end sub
75
75
  end namespace
76
- 'Allows chaining multiple promise operations in a row in a clean syntax
77
76
  namespace rooibos.promises
78
- function chain(initialPromise as object, context = "__INVALID__" as object) as object
77
+ 'Allows chaining multiple promise operations in a row in a clean syntax
78
+ function chain(initialPromise as object, context = "__INVALID__" as object) as object
79
79
  end function
80
80
  end namespace
81
- ' Makes sure the value supplied is a promise
82
81
  namespace rooibos.promises
83
- function ensurePromise(value as object) as object
82
+ ' Makes sure the value supplied is a promise
83
+ function ensurePromise(value as object) as object
84
84
  end function
85
85
  end namespace
86
86
  namespace rooibos.promises
@@ -151,14 +151,14 @@ namespace rooibos.promises.internal
151
151
  sub delay(callback as function, context as dynamic, duration = 0.0001 as float)
152
152
  end sub
153
153
  ' Observes a node field using observeFieldScoped
154
- ' @param {roSGNode} node - The node to apply the observer
154
+ ' @parrooibos.promises.internal.roSGNodeNode} node - The node to apply the observer
155
155
  ' @param {String} field - The name of the field to be monitored.
156
156
  ' @param {Dynamic} callback - The name or message port to be executed when the value of the field changes.
157
157
  ' @return true if field could be observed, false if not
158
158
  function observeFieldScoped(node as object, field as string, callback as dynamic, infoFields = [] as object)
159
159
  end function
160
160
  ' Unobserve a node field using unobserveFieldScoped
161
- ' @param {roSGNode} node - The node to remove the observer from
161
+ ' @parrooibos.promises.internal.roSGNodeNode} node - The node to remove the observer from
162
162
  ' @param {String} field - The name of the field to no longer be monitored.
163
163
  ' @return true if field could be unobserved, false if not
164
164
  function unobserveFieldScoped(node as object, field as string)
@@ -40,7 +40,7 @@ namespace rooibos
40
40
  ' leading to an instant exit of the application
41
41
 
42
42
  ' Give the io port time to finish sending all the logs
43
- sleep(400)
43
+ sleep(runner.config.shutdownDelay)
44
44
  end
45
45
  end if
46
46
  end if
@@ -121,7 +121,11 @@ class CodeCoverageProcessor {
121
121
  },
122
122
  ExpressionStatement: (ds, parent, owner, key) => {
123
123
  this.addStatement(ds);
124
- this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key);
124
+ //bsc's class transpiler assumes a `super()` call sits at index 0 of the constructor body, and
125
+ //injects field initializers immediately after it. Inserting our coverage statement *before*
126
+ //`super()` would shift it off index 0, causing field initializers to be emitted before the
127
+ //`super()` call. So for `super()` we insert coverage *after* the call instead.
128
+ this.convertStatementToCoverageStatement(ds, CodeCoverageLineType.code, owner, key, this.isSuperCall(ds));
125
129
  }
126
130
  }), { walkMode: brighterscript_1.WalkMode.visitAllRecursive });
127
131
  const coverageMapObject = {};
@@ -132,14 +136,27 @@ class CodeCoverageProcessor {
132
136
  this.filePathMap[this.fileId] = file.pkgPath;
133
137
  this.addBrsAPIText(file, astEditor);
134
138
  }
135
- convertStatementToCoverageStatement(statement, coverageType, owner, key) {
139
+ /**
140
+ * Is this statement a call to `super()`? (i.e. the parent-constructor call in a derived class constructor)
141
+ */
142
+ isSuperCall(statement) {
143
+ var _a, _b, _c;
144
+ return (0, brighterscript_1.isExpressionStatement)(statement) &&
145
+ (0, brighterscript_1.isCallExpression)(statement.expression) &&
146
+ ((_c = (_b = (_a = brighterscript_1.util.findBeginningVariableExpression(statement.expression.callee)) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.text) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === 'super';
147
+ }
148
+ /**
149
+ * Insert a coverage-tracking statement adjacent to the given statement.
150
+ * When `insertAfter` is true, the coverage statement is inserted *after* the given statement rather than before it.
151
+ */
152
+ convertStatementToCoverageStatement(statement, coverageType, owner, key, insertAfter = false) {
136
153
  if (this.processedStatements.has(statement) || this.addedStatements.has(statement)) {
137
154
  return;
138
155
  }
139
156
  const lineNumber = statement.range.start.line;
140
157
  this.coverageMap.set(lineNumber, coverageType);
141
158
  const parsed = brighterscript_1.Parser.parse(this.getFuncCallText(lineNumber, coverageType)).ast.statements[0];
142
- this.astEditor.arraySplice(owner, key, 0, parsed);
159
+ this.astEditor.arraySplice(owner, insertAfter ? key + 1 : key, 0, parsed);
143
160
  this.addedStatements.add(parsed);
144
161
  // store the statement in a set to avoid handling again after inserting statement above
145
162
  this.processedStatements.add(statement);
@@ -101,15 +101,35 @@ class MockUtil {
101
101
  }
102
102
  gatherGlobalMethodMocks(testSuite) {
103
103
  // console.log('gathering global method mocks for testSuite', testSuite.name);
104
+ const processedFuncs = new Set();
104
105
  for (let group of [...testSuite.testGroups.values()].filter((tg) => tg.isIncluded)) {
106
+ for (const hookName of [group.setupFunctionName, group.tearDownFunctionName, group.beforeEachFunctionName, group.afterEachFunctionName]) {
107
+ if (hookName) {
108
+ const key = hookName.toLowerCase();
109
+ if (!processedFuncs.has(key)) {
110
+ this.gatherMockedFunctionByName(testSuite, hookName);
111
+ processedFuncs.add(key);
112
+ }
113
+ }
114
+ }
105
115
  for (let testCase of [...group.testCases].filter((tc) => tc.isIncluded)) {
106
- this.gatherMockedGlobalMethods(testSuite, testCase);
116
+ const key = testCase.funcName.toLowerCase();
117
+ if (!processedFuncs.has(key)) {
118
+ this.gatherMockedGlobalMethods(testSuite, testCase);
119
+ processedFuncs.add(key);
120
+ }
107
121
  }
108
122
  }
109
123
  }
110
124
  gatherMockedGlobalMethods(testSuite, testCase) {
125
+ this.gatherMockedFunctionByName(testSuite, testCase.funcName);
126
+ }
127
+ gatherMockedFunctionByName(testSuite, funcName) {
111
128
  try {
112
- let func = testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === testCase.funcName.toLowerCase());
129
+ let func = testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === funcName.toLowerCase());
130
+ if (!func) {
131
+ return;
132
+ }
113
133
  func.walk((0, brighterscript_1.createVisitor)({
114
134
  ExpressionStatement: (expressionStatement, parent, owner) => {
115
135
  let callExpression = expressionStatement.expression;
@@ -112,7 +112,7 @@ class RooibosSession {
112
112
  }
113
113
  }
114
114
  updateRunTimeConfigFunction(classStatement, editor) {
115
- var _a, _b, _c;
115
+ var _a, _b, _c, _d;
116
116
  let method = classStatement.methods.find((m) => m.name.text === 'getRuntimeConfig');
117
117
  if (method) {
118
118
  editor.addToArray(method.func.body.statements, method.func.body.statements.length, brighterscript_1.Parser.parse((0, undent_1.default) `
@@ -130,6 +130,7 @@ class RooibosSession {
130
130
  "colorizeOutput": ${this.config.colorizeOutput ? 'true' : 'false'}
131
131
  "throwOnFailedAssertion": ${this.config.throwOnFailedAssertion ? 'true' : 'false'}
132
132
  "keepAppOpen": ${this.config.keepAppOpen === undefined || this.config.keepAppOpen ? 'true' : 'false'}
133
+ "shutdownDelay": ${(_d = this.config.shutdownDelay) !== null && _d !== void 0 ? _d : 400}
133
134
  "isRecordingCodeCoverage": ${this.config.isRecordingCodeCoverage ? 'true' : 'false'}
134
135
  }
135
136
  `).ast.statements[0]);
@@ -23,15 +23,28 @@ class TestGroup extends TestSuite_1.TestBlock {
23
23
  this.hasAsyncTests || (this.hasAsyncTests = testCase.isAsync);
24
24
  }
25
25
  modifyAssertions(testCase, noEarlyExit, editor, namespaceLookup, scope) {
26
+ let func = this.testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === testCase.funcName.toLowerCase());
27
+ this.modifyAssertionsInFunction(func, noEarlyExit, editor, namespaceLookup, scope);
28
+ }
29
+ modifyAssertionsForHook(hookFuncName, noEarlyExit, editor, namespaceLookup, scope) {
30
+ if (!hookFuncName) {
31
+ return;
32
+ }
33
+ let func = this.testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === hookFuncName.toLowerCase());
34
+ if (!func) {
35
+ return;
36
+ }
37
+ this.modifyAssertionsInFunction(func, noEarlyExit, editor, namespaceLookup, scope);
38
+ }
39
+ modifyAssertionsInFunction(func, noEarlyExit, editor, namespaceLookup, scope) {
26
40
  //for each method
27
41
  //if assertion
28
42
  //wrap with if is not fail
29
43
  //add line number as last param
30
44
  try {
31
- let func = this.testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === testCase.funcName.toLowerCase());
32
45
  func.walk((0, brighterscript_1.createVisitor)({
33
46
  ExpressionStatement: (expressionStatement, parent, owner, key) => {
34
- var _a, _b;
47
+ var _a, _b, _c;
35
48
  let callExpression = expressionStatement.expression;
36
49
  if ((0, brighterscript_1.isCallExpression)(callExpression) && (0, brighterscript_1.isDottedGetExpression)(callExpression.callee)) {
37
50
  let dge = callExpression.callee;
@@ -44,6 +57,14 @@ class TestGroup extends TestSuite_1.TestBlock {
44
57
  // `someMagicVar`.assert*(...)
45
58
  const callPath = (_b = brighterscript_1.util.getAllDottedGetParts(callExpression.callee.obj)) === null || _b === void 0 ? void 0 : _b.map((part) => part.text).join('.');
46
59
  if (callPath) {
60
+ // Skip if callPath starts with a namespace name — this is a namespace
61
+ // function call (e.g. some.space.assertEqual()), not a method call on
62
+ // an object like m. Injecting assertion tracking on a namespace prefix
63
+ // produces invalid output (e.g. some_space.currentAssertLineNumber).
64
+ const callPathFirstPart = callPath.split('.')[0].toLowerCase();
65
+ if (namespaceLookup.has(callPathFirstPart) || ((_c = scope === null || scope === void 0 ? void 0 : scope.namespaceLookup) === null || _c === void 0 ? void 0 : _c.has(callPathFirstPart))) {
66
+ return;
67
+ }
47
68
  if (dge.name.text === 'stubCall') {
48
69
  this.modifyModernRooibosExpectCallExpression(callExpression, editor, namespaceLookup, scope);
49
70
  return expressionStatement;
@@ -14,6 +14,7 @@ function addDiagnosticForStatement(file, code, message, statement, severity = br
14
14
  function addDiagnosticForAnnotation(file, code, message, annotation, severity = brighterscript_1.DiagnosticSeverity.Error, endChar) {
15
15
  let line = annotation.range.start.line;
16
16
  let col = annotation.range.start.character;
17
+ // TODO: Fix the end character to be the end of the annotation, not the start + 9999
17
18
  file.addDiagnostics([createDiagnostic(file, code, message, line, col, annotation.range.end.line, annotation.range.end.character + 9999, severity)]);
18
19
  }
19
20
  function createDiagnostic(bscFile, code, message, startLine = 0, startCol = 99999, endLine = 0, endCol = 99999, severity = brighterscript_1.DiagnosticSeverity.Error) {
package/dist/plugin.js CHANGED
@@ -144,8 +144,18 @@ class RooibosPlugin {
144
144
  console.warn(`WARNING: testSuite "${testSuite.name}" is marked as noEarlyExit`);
145
145
  }
146
146
  const modifiedTestCases = new Set();
147
+ const modifiedHookFunctions = new Set();
147
148
  testSuite.addDataFunctions(event.editor);
148
149
  for (let group of [...testSuite.testGroups.values()].filter((tg) => tg.isIncluded)) {
150
+ for (const hookName of [group.setupFunctionName, group.tearDownFunctionName, group.beforeEachFunctionName, group.afterEachFunctionName]) {
151
+ if (hookName) {
152
+ const hookKey = group.testSuite.generatedNodeName + group.file.pkgPath + hookName.toLowerCase();
153
+ if (!modifiedHookFunctions.has(hookKey)) {
154
+ group.modifyAssertionsForHook(hookName, noEarlyExit, event.editor, this.session.namespaceLookup, scope);
155
+ modifiedHookFunctions.add(hookKey);
156
+ }
157
+ }
158
+ }
149
159
  for (let testCase of [...group.testCases].filter((tc) => tc.isIncluded)) {
150
160
  let caseName = group.testSuite.generatedNodeName + group.file.pkgPath + testCase.funcName;
151
161
  if (!modifiedTestCases.has(caseName)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rooibos-roku",
3
- "version": "5.16.3",
3
+ "version": "5.16.5",
4
4
  "description": "simple, flexible, fun brightscript test framework for roku scenegraph apps - roku brighterscript plugin",
5
5
  "scripts": {
6
6
  "preversion": "npm run build && npm run lint && npm run test",
@@ -14,17 +14,18 @@
14
14
  "docs": "jsdoc -c jsdoc.json -d .tmp/docs --verbose",
15
15
  "test": "nyc mocha",
16
16
  "test:nocover": "mocha",
17
- "audit": "npm audit",
17
+ "audit": "npm audit || audit-ci --config ./audit-ci.jsonc",
18
18
  "cli": "ts-node src/cli.ts",
19
+ "cli:device": "ts-node src/cli.ts --project test-project/bsconfig.device.json",
19
20
  "package": "npm run build && npm pack"
20
21
  },
21
22
  "dependencies": {
22
- "brighterscript": "^0.72.2",
23
+ "brighterscript": "^0.73.3",
23
24
  "fast-glob": "^3.2.12",
24
25
  "fs-extra": "^10.1.0",
25
26
  "minimatch": "^3.0.4",
26
- "roku-debug": "^0.23.8",
27
- "roku-deploy": "^3.17.4",
27
+ "roku-debug": "^0.24.3",
28
+ "roku-deploy": "^4.0.0-alpha.5",
28
29
  "rooibos_promises": "npm:@rokucommunity/promises@^0.5.0",
29
30
  "source-map": "^0.7.3",
30
31
  "undent": "^1.0.1",
@@ -33,7 +34,7 @@
33
34
  "yargs": "^16.2.0"
34
35
  },
35
36
  "devDependencies": {
36
- "@rokucommunity/bslint": "^0.8.43",
37
+ "@rokucommunity/bslint": "^0.8.46",
37
38
  "@types/chai": "^4.1.2",
38
39
  "@types/events": "^3.0.0",
39
40
  "@types/fs-extra": "^5.0.1",
@@ -43,6 +44,7 @@
43
44
  "@types/yargs": "^15.0.5",
44
45
  "@typescript-eslint/eslint-plugin": "^5.27.0",
45
46
  "@typescript-eslint/parser": "^5.27.0",
47
+ "audit-ci": "^7.1.0",
46
48
  "brighterscript-jsdocs-plugin": "^0.7.3",
47
49
  "chai": "^4.2.0",
48
50
  "docdash": "^2.0.2",
@@ -54,13 +56,16 @@
54
56
  "jsdoc": "^4.0.5",
55
57
  "mocha": "^10.8.2",
56
58
  "nyc": "^15.1.0",
57
- "ropm": "^0.11.7",
59
+ "ropm": "^0.11.11",
58
60
  "source-map-support": "^0.5.13",
59
61
  "ts-node": "^10.7.0",
60
62
  "typescript": "^4.7.2"
61
63
  },
62
64
  "overrides": {
63
- "serialize-javascript": "^7.0.5"
65
+ "serialize-javascript": "^7.0.5",
66
+ "js-yaml@^3": "^3.15.2",
67
+ "js-yaml@^4": "^4.3.2",
68
+ "browserslist": "^4.28.8"
64
69
  },
65
70
  "main": "dist/plugin.js",
66
71
  "bin": {