@meri-imperiumi/signalk-triplogger 1.3.0 → 1.3.2
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/.editorconfig +5 -0
- package/.eslintrc.json +7 -1
- package/.github/workflows/publish.yml +10 -6
- package/.github/workflows/signalk-ci.yml +11 -0
- package/.github/workflows/test.yml +2 -10
- package/CHANGELOG.md +42 -0
- package/README.md +1 -12
- package/doc/appicon.png +0 -0
- package/package.json +10 -3
- package/tests/fake-app.js +78 -0
- package/tests/plugin.test.js +268 -0
- package/.github/dependabot.yml +0 -11
package/.editorconfig
ADDED
package/.eslintrc.json
CHANGED
|
@@ -5,14 +5,19 @@ on:
|
|
|
5
5
|
tags:
|
|
6
6
|
- "*"
|
|
7
7
|
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write # Required for OIDC
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
8
12
|
jobs:
|
|
9
13
|
build:
|
|
10
14
|
runs-on: ubuntu-latest
|
|
11
15
|
steps:
|
|
12
16
|
- uses: actions/checkout@v3.0.2
|
|
13
|
-
- uses: actions/setup-node@v3.
|
|
17
|
+
- uses: actions/setup-node@v3.1.1
|
|
14
18
|
with:
|
|
15
|
-
node-version:
|
|
19
|
+
node-version: 24
|
|
20
|
+
package-manager-cache: false # never use caching in release builds
|
|
16
21
|
- run: npm install
|
|
17
22
|
- run: npm test
|
|
18
23
|
|
|
@@ -21,10 +26,9 @@ jobs:
|
|
|
21
26
|
runs-on: ubuntu-latest
|
|
22
27
|
steps:
|
|
23
28
|
- uses: actions/checkout@v3.0.2
|
|
24
|
-
- uses: actions/setup-node@v3.
|
|
29
|
+
- uses: actions/setup-node@v3.1.1
|
|
25
30
|
with:
|
|
26
|
-
node-version:
|
|
31
|
+
node-version: 24
|
|
27
32
|
registry-url: https://registry.npmjs.org/
|
|
33
|
+
package-manager-cache: false # never use caching in release builds
|
|
28
34
|
- run: npm publish
|
|
29
|
-
env:
|
|
30
|
-
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
@@ -8,22 +8,14 @@ jobs:
|
|
|
8
8
|
runs-on: ubuntu-latest
|
|
9
9
|
strategy:
|
|
10
10
|
matrix:
|
|
11
|
-
node-version: [
|
|
11
|
+
node-version: [24.x]
|
|
12
12
|
steps:
|
|
13
13
|
- uses: actions/checkout@v3.0.2
|
|
14
14
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
15
|
-
uses: actions/setup-node@v3.
|
|
15
|
+
uses: actions/setup-node@v3.1.1
|
|
16
16
|
with:
|
|
17
17
|
node-version: ${{ matrix.node-version }}
|
|
18
18
|
- run: npm install
|
|
19
19
|
- run: npm test
|
|
20
20
|
env:
|
|
21
21
|
CI: true
|
|
22
|
-
merge-me:
|
|
23
|
-
name: Auto-merge dependency updates
|
|
24
|
-
needs: test
|
|
25
|
-
runs-on: ubuntu-latest
|
|
26
|
-
steps:
|
|
27
|
-
- uses: ridedott/merge-me-action@v2.10.22
|
|
28
|
-
with:
|
|
29
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.3.2] - 2026-08-23
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Added smoke tests for the plugin entry point (start/stop, subscription,
|
|
7
|
+
state-change resets, position logging, totals)
|
|
8
|
+
|
|
9
|
+
## [1.3.1] - 2026-06-16
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Added application icon for SK appstore
|
|
13
|
+
|
|
14
|
+
## [1.3.0] - 2026-06-10
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Don't start a new trip at startup if there's an ongoing one
|
|
18
|
+
|
|
19
|
+
## [1.2.0] - 2023-04-27
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- Added support for populating `navigation.log` for totals
|
|
23
|
+
|
|
24
|
+
## [1.1.1] - 2023-02-28
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- Fix issue when `navigation.position` doesn't contain coordinates
|
|
28
|
+
|
|
29
|
+
## [1.1.0] - 2022-03-04
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- Persist logs for current day/month/year, also by sailing/motoring
|
|
33
|
+
|
|
34
|
+
## [1.0.1] - 2022-02-18
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- Fix issue with reading lat and lon
|
|
38
|
+
|
|
39
|
+
## [1.0.0] - 2022-02-18
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- Initial version, logging in-memory only
|
package/README.md
CHANGED
|
@@ -6,15 +6,4 @@ The plugin monitors vessel `navigation.state` as set by the [signalk-autostate](
|
|
|
6
6
|
|
|
7
7
|
## Changes
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- Don't start a new trip at startup if there's an ongoing one
|
|
11
|
-
* 1.2.0 (April 27th 2023)
|
|
12
|
-
- Added support for populating `navigation.log` for totals
|
|
13
|
-
* 1.1.1 (February 28th 2023)
|
|
14
|
-
- Fix issue when `navigation.position` doesn't contain coordinates
|
|
15
|
-
* 1.1.0 (March 4th 2022)
|
|
16
|
-
- Persist logs for current day/month/year, also by sailing/motoring
|
|
17
|
-
* 1.0.1 (February 18th 2022)
|
|
18
|
-
- Fix issue with reading lat and lon
|
|
19
|
-
* 1.0.0 (February 18th 2022)
|
|
20
|
-
- Initial version, logging in-memory only
|
|
9
|
+
See [Changelog](CHANGELOG.md)
|
package/doc/appicon.png
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meri-imperiumi/signalk-triplogger",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Log the length of the current trip",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"test": "node --test --test-force-exit \"tests/*.test.js\" && eslint .",
|
|
8
|
+
"lint": "eslint ."
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
"signalk-category-utility"
|
|
17
17
|
],
|
|
18
18
|
"signalk-plugin-enabled-by-default": true,
|
|
19
|
+
"signalk": {
|
|
20
|
+
"appIcon": "./doc/appicon.png",
|
|
21
|
+
"displayName": "Automatic Trip Length Logger",
|
|
22
|
+
"requires": [
|
|
23
|
+
"@meri-imperiumi/signalk-autostate"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
19
26
|
"author": "Henri Bergius <henri.bergius@iki.fi>",
|
|
20
27
|
"license": "MIT",
|
|
21
28
|
"bugs": {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test fakes: a mock Signal K app matching the patterns used by the
|
|
3
|
+
* signalk-aprsfi-ais-reporter test suite, adapted for the triplogger's
|
|
4
|
+
* needs (subscriptionmanager + handleMessage + setPluginStatus + data dir).
|
|
5
|
+
*
|
|
6
|
+
* @file fake-app.js
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/* eslint-disable class-methods-use-this */
|
|
10
|
+
|
|
11
|
+
const { join } = require('node:path');
|
|
12
|
+
const { mkdtempSync } = require('node:fs');
|
|
13
|
+
const { tmpdir } = require('node:os');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Minimal Signal K app fake with subscriptionmanager + handleMessage +
|
|
17
|
+
* setPluginStatus + setProviderStatus + getDataDirPath + error.
|
|
18
|
+
*
|
|
19
|
+
* The data path is a fresh temp directory per instance so the Logger can
|
|
20
|
+
* write log files without touching the project tree.
|
|
21
|
+
*/
|
|
22
|
+
class FakeSignalKApp {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.selfId = 'urn:mrn:imo:mmsi:230999999';
|
|
25
|
+
this.subscriptionmanager = {
|
|
26
|
+
subscriptions: [],
|
|
27
|
+
subscribe(subscription, unsubscribes, onError, onDelta) {
|
|
28
|
+
this.subscriptions.push({ subscription, onDelta });
|
|
29
|
+
unsubscribes.push(() => {
|
|
30
|
+
const idx = this.subscriptions.findIndex(
|
|
31
|
+
(s) => s.subscription === subscription,
|
|
32
|
+
);
|
|
33
|
+
if (idx >= 0) this.subscriptions.splice(idx, 1);
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
this.statusMessages = [];
|
|
38
|
+
this.errors = [];
|
|
39
|
+
this.messages = [];
|
|
40
|
+
this.dataPath = mkdtempSync(join(tmpdir(), 'triplogger-test-'));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getDataDirPath() {
|
|
44
|
+
return this.dataPath;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setPluginStatus = (msg) => {
|
|
48
|
+
this.statusMessages.push({ type: 'status', msg });
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
setProviderStatus = (msg) => {
|
|
52
|
+
this.statusMessages.push({ type: 'status', msg });
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
handleMessage = (sourceId, message) => {
|
|
56
|
+
this.messages.push({ sourceId, message });
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
debug() {}
|
|
60
|
+
|
|
61
|
+
error(msg) {
|
|
62
|
+
this.errors.push(msg);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Emits a delta into the subscriptionmanager's registered handlers, for
|
|
68
|
+
* tests that want to feed navigation values.
|
|
69
|
+
*
|
|
70
|
+
* @param {FakeSignalKApp} app
|
|
71
|
+
* @param {object} delta
|
|
72
|
+
* @returns {void}
|
|
73
|
+
*/
|
|
74
|
+
function emitDelta(app, delta) {
|
|
75
|
+
app.subscriptionmanager.subscriptions.forEach(({ onDelta }) => onDelta(delta));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { FakeSignalKApp, emitDelta };
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke tests for the plugin entry point (plugin shape, start/stop,
|
|
3
|
+
* subscription, state-change resets, position logging, totals).
|
|
4
|
+
* @file plugin.test.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const test = require('node:test');
|
|
8
|
+
const assert = require('node:assert/strict');
|
|
9
|
+
const { readFileSync, writeFileSync } = require('node:fs');
|
|
10
|
+
const { join } = require('node:path');
|
|
11
|
+
|
|
12
|
+
const makePlugin = require('../index');
|
|
13
|
+
const { FakeSignalKApp, emitDelta } = require('./fake-app');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Poll until `predicate` returns truthy, retrying every ~10ms up to `timeout`
|
|
17
|
+
* milliseconds. Resolves true on success, false on timeout. Useful for
|
|
18
|
+
* awaiting async plugin side-effects (file I/O) without fixed sleeps.
|
|
19
|
+
*
|
|
20
|
+
* @param {() => boolean} predicate
|
|
21
|
+
* @param {number} [timeout]
|
|
22
|
+
* @returns {Promise<boolean>}
|
|
23
|
+
*/
|
|
24
|
+
function waitFor(predicate, timeout = 500) {
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
const start = Date.now();
|
|
27
|
+
const tick = () => {
|
|
28
|
+
if (predicate()) {
|
|
29
|
+
resolve(true);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (Date.now() - start >= timeout) {
|
|
33
|
+
resolve(false);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
setTimeout(tick, 10);
|
|
37
|
+
};
|
|
38
|
+
tick();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build a started plugin instance wired to a fresh fake app with the
|
|
44
|
+
* given settings (defaulted).
|
|
45
|
+
*
|
|
46
|
+
* @param {object} [overrides]
|
|
47
|
+
* @returns {{app: FakeSignalKApp, plugin: object}}
|
|
48
|
+
*/
|
|
49
|
+
function makeStarted(overrides) {
|
|
50
|
+
const app = new FakeSignalKApp();
|
|
51
|
+
const plugin = makePlugin(app);
|
|
52
|
+
plugin.start({
|
|
53
|
+
update_interval: 10000,
|
|
54
|
+
totals: true,
|
|
55
|
+
totals_base: 0,
|
|
56
|
+
...overrides,
|
|
57
|
+
});
|
|
58
|
+
return { app, plugin };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Read a log file written by the Logger from the app's data dir.
|
|
63
|
+
*
|
|
64
|
+
* @param {FakeSignalKApp} app
|
|
65
|
+
* @param {string} name
|
|
66
|
+
* @returns {object}
|
|
67
|
+
*/
|
|
68
|
+
function readLog(app, name) {
|
|
69
|
+
return JSON.parse(readFileSync(join(app.dataPath, `${name}.json`), 'utf-8'));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Pre-seed a log file in the app's data dir before start, so the Logger
|
|
74
|
+
* loads an existing state instead of starting fresh. Used to make the
|
|
75
|
+
* "new trip" transition deterministic without racing two state emits.
|
|
76
|
+
*
|
|
77
|
+
* @param {FakeSignalKApp} app
|
|
78
|
+
* @param {string} name
|
|
79
|
+
* @param {object} contents
|
|
80
|
+
* @returns {void}
|
|
81
|
+
*/
|
|
82
|
+
function seedLog(app, name, contents) {
|
|
83
|
+
writeFileSync(join(app.dataPath, `${name}.json`), JSON.stringify(contents, null, 2));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
test('creates a plugin object with the right id/name', () => {
|
|
87
|
+
const app = new FakeSignalKApp();
|
|
88
|
+
const plugin = makePlugin(app);
|
|
89
|
+
assert.strictEqual(plugin.id, 'signalk-triplogger');
|
|
90
|
+
assert.strictEqual(plugin.name, 'Trip logger');
|
|
91
|
+
assert.ok(plugin.description);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('schema is a JSON object with the expected options', () => {
|
|
95
|
+
const app = new FakeSignalKApp();
|
|
96
|
+
const plugin = makePlugin(app);
|
|
97
|
+
assert.strictEqual(plugin.schema.type, 'object');
|
|
98
|
+
assert.ok(plugin.schema.properties.update_interval);
|
|
99
|
+
assert.ok(plugin.schema.properties.totals);
|
|
100
|
+
assert.ok(plugin.schema.properties.totals_base);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('start subscribes to navigation.state and navigation.position', () => {
|
|
104
|
+
const { app, plugin } = makeStarted();
|
|
105
|
+
assert.strictEqual(app.subscriptionmanager.subscriptions.length, 1);
|
|
106
|
+
const sub = app.subscriptionmanager.subscriptions[0].subscription;
|
|
107
|
+
assert.strictEqual(sub.context, 'vessels.self');
|
|
108
|
+
const paths = sub.subscribe.map((s) => s.path);
|
|
109
|
+
assert.ok(paths.includes('navigation.state'));
|
|
110
|
+
assert.ok(paths.includes('navigation.position'));
|
|
111
|
+
plugin.stop();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('start sets an initial status message', () => {
|
|
115
|
+
const { app, plugin } = makeStarted();
|
|
116
|
+
assert.ok(
|
|
117
|
+
app.statusMessages.some(
|
|
118
|
+
(m) => m.type === 'status' && /waiting/i.test(m.msg),
|
|
119
|
+
),
|
|
120
|
+
`got ${JSON.stringify(app.statusMessages)}`,
|
|
121
|
+
);
|
|
122
|
+
plugin.stop();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('transitioning into a trip resets the log and posts a status', async () => {
|
|
126
|
+
const app = new FakeSignalKApp();
|
|
127
|
+
// A fresh current log is considered already in-trip. To make the
|
|
128
|
+
// transition into a trip deterministic (instead of racing two async
|
|
129
|
+
// state emits), seed an *ended* current log so inTrip() starts false,
|
|
130
|
+
// then emit a single trip-starting state.
|
|
131
|
+
seedLog(app, 'current', {
|
|
132
|
+
started: '2026-01-01T00:00:00.000Z',
|
|
133
|
+
ended: '2026-01-01T01:00:00.000Z',
|
|
134
|
+
total: 0,
|
|
135
|
+
states: {},
|
|
136
|
+
});
|
|
137
|
+
const plugin = makePlugin(app);
|
|
138
|
+
plugin.start({});
|
|
139
|
+
emitDelta(app, {
|
|
140
|
+
context: 'vessels.self',
|
|
141
|
+
updates: [
|
|
142
|
+
{ values: [{ path: 'navigation.state', value: 'sailing' }] },
|
|
143
|
+
],
|
|
144
|
+
});
|
|
145
|
+
await waitFor(
|
|
146
|
+
() => app.statusMessages.some((m) => /new trip/i.test(m.msg)),
|
|
147
|
+
);
|
|
148
|
+
plugin.stop();
|
|
149
|
+
assert.ok(
|
|
150
|
+
app.statusMessages.some(
|
|
151
|
+
(m) => m.type === 'status' && /new trip/i.test(m.msg),
|
|
152
|
+
),
|
|
153
|
+
`got ${JSON.stringify(app.statusMessages)}`,
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('position updates append distance to the trip log and emit deltas', async () => {
|
|
158
|
+
const app = new FakeSignalKApp();
|
|
159
|
+
// Seed an ended current log so start isn't considered in-trip, then emit
|
|
160
|
+
// a single trip-starting state. This avoids racing two async state emits.
|
|
161
|
+
seedLog(app, 'current', {
|
|
162
|
+
started: '2026-01-01T00:00:00.000Z',
|
|
163
|
+
ended: '2026-01-01T01:00:00.000Z',
|
|
164
|
+
total: 0,
|
|
165
|
+
states: {},
|
|
166
|
+
});
|
|
167
|
+
const plugin = makePlugin(app);
|
|
168
|
+
plugin.start({ totals: true });
|
|
169
|
+
emitDelta(app, {
|
|
170
|
+
context: 'vessels.self',
|
|
171
|
+
updates: [{ values: [{ path: 'navigation.state', value: 'sailing' }] }],
|
|
172
|
+
});
|
|
173
|
+
// Wait for the new-trip reset to settle before sending positions.
|
|
174
|
+
await waitFor(
|
|
175
|
+
() => app.statusMessages.some((m) => /new trip/i.test(m.msg)),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// First position just establishes lastPosition; no distance yet.
|
|
179
|
+
emitDelta(app, {
|
|
180
|
+
context: 'vessels.self',
|
|
181
|
+
updates: [
|
|
182
|
+
{ values: [{ path: 'navigation.position', value: { latitude: 60.0, longitude: 24.0 } }] },
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Second position ~111m east of the first → appendTrip writes log files.
|
|
187
|
+
emitDelta(app, {
|
|
188
|
+
context: 'vessels.self',
|
|
189
|
+
updates: [
|
|
190
|
+
{ values: [{ path: 'navigation.position', value: { latitude: 60.001, longitude: 24.0 } }] },
|
|
191
|
+
],
|
|
192
|
+
});
|
|
193
|
+
await waitFor(() => {
|
|
194
|
+
const tripValue = app.messages
|
|
195
|
+
.flatMap(({ message }) => message.updates.flatMap((u) => u.values))
|
|
196
|
+
.find((v) => v.path === 'navigation.trip.log' && v.value > 0);
|
|
197
|
+
return !!tripValue;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
plugin.stop();
|
|
201
|
+
|
|
202
|
+
// A trip.log delta should have been published to the app.
|
|
203
|
+
const tripMsgs = app.messages.filter(
|
|
204
|
+
({ message }) => message.updates.some((u) => u.values.some((v) => v.path === 'navigation.trip.log')),
|
|
205
|
+
);
|
|
206
|
+
assert.ok(tripMsgs.length > 0, 'no navigation.trip.log delta was emitted');
|
|
207
|
+
|
|
208
|
+
// The current trip log file should be persisted with a positive total.
|
|
209
|
+
const current = readLog(app, 'current');
|
|
210
|
+
assert.ok(current.total > 0, `current.total was ${current.total}`);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('totals option also publishes navigation.log', async () => {
|
|
214
|
+
const app = new FakeSignalKApp();
|
|
215
|
+
seedLog(app, 'current', {
|
|
216
|
+
started: '2026-01-01T00:00:00.000Z',
|
|
217
|
+
ended: '2026-01-01T01:00:00.000Z',
|
|
218
|
+
total: 0,
|
|
219
|
+
states: {},
|
|
220
|
+
});
|
|
221
|
+
const plugin = makePlugin(app);
|
|
222
|
+
plugin.start({ totals: true, totals_base: 100 });
|
|
223
|
+
emitDelta(app, {
|
|
224
|
+
context: 'vessels.self',
|
|
225
|
+
updates: [{ values: [{ path: 'navigation.state', value: 'motoring' }] }],
|
|
226
|
+
});
|
|
227
|
+
await waitFor(
|
|
228
|
+
() => app.statusMessages.some((m) => /new trip/i.test(m.msg)),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
emitDelta(app, {
|
|
232
|
+
context: 'vessels.self',
|
|
233
|
+
updates: [
|
|
234
|
+
{ values: [{ path: 'navigation.position', value: { latitude: 60.0, longitude: 24.0 } }] },
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
emitDelta(app, {
|
|
238
|
+
context: 'vessels.self',
|
|
239
|
+
updates: [
|
|
240
|
+
{ values: [{ path: 'navigation.position', value: { latitude: 60.002, longitude: 24.0 } }] },
|
|
241
|
+
],
|
|
242
|
+
});
|
|
243
|
+
await waitFor(() => {
|
|
244
|
+
const logValue = app.messages
|
|
245
|
+
.flatMap(({ message }) => message.updates.flatMap((u) => u.values))
|
|
246
|
+
.find((v) => v.path === 'navigation.log' && v.value > 100);
|
|
247
|
+
return !!logValue;
|
|
248
|
+
});
|
|
249
|
+
plugin.stop();
|
|
250
|
+
|
|
251
|
+
const logMsgs = app.messages.filter(
|
|
252
|
+
({ message }) => message.updates.some((u) => u.values.some((v) => v.path === 'navigation.log')),
|
|
253
|
+
);
|
|
254
|
+
assert.ok(logMsgs.length > 0, 'no navigation.log delta was emitted');
|
|
255
|
+
const last = logMsgs[logMsgs.length - 1];
|
|
256
|
+
const { value } = last.message.updates
|
|
257
|
+
.flatMap((u) => u.values)
|
|
258
|
+
.find((v) => v.path === 'navigation.log');
|
|
259
|
+
// totals_base of 100 should be added on top of the logged distance.
|
|
260
|
+
assert.ok(value > 100, `navigation.log value was ${value}`);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test('stop clears subscriptions', () => {
|
|
264
|
+
const { app, plugin } = makeStarted();
|
|
265
|
+
assert.strictEqual(app.subscriptionmanager.subscriptions.length, 1);
|
|
266
|
+
plugin.stop();
|
|
267
|
+
assert.strictEqual(app.subscriptionmanager.subscriptions.length, 0);
|
|
268
|
+
});
|
package/.github/dependabot.yml
DELETED