node-osc 11.1.0 → 11.2.0

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.
Files changed (69) hide show
  1. package/.gitattributes +11 -0
  2. package/.github/workflows/bump-version.yml +2 -0
  3. package/.github/workflows/nodejs.yml +3 -0
  4. package/LICENSE +201 -165
  5. package/README.md +135 -42
  6. package/dist/lib/Bundle.js +66 -0
  7. package/dist/lib/Client.js +137 -22
  8. package/dist/lib/Message.js +87 -1
  9. package/dist/lib/Server.js +117 -6
  10. package/dist/lib/index.js +3 -0
  11. package/dist/lib/internal/decode.js +4 -4
  12. package/{lib/internal/osc.mjs → dist/lib/osc.js} +94 -23
  13. package/dist/test/lib/osc.js +395 -0
  14. package/dist/test/test-client.js +152 -0
  15. package/dist/test/test-e2e.js +9 -3
  16. package/dist/test/test-encode-decode.js +849 -0
  17. package/dist/test/test-error-handling.js +116 -0
  18. package/dist/test/test-osc-internal.js +399 -41
  19. package/dist/test/test-promises.js +250 -0
  20. package/dist/test/test-types.js +42 -0
  21. package/dist/test/util.js +15 -8
  22. package/docs/API.md +477 -0
  23. package/docs/GUIDE.md +605 -0
  24. package/examples/README.md +119 -0
  25. package/examples/async-await.mjs +57 -0
  26. package/examples/bundle-example.mjs +92 -0
  27. package/examples/client.js +22 -5
  28. package/examples/error-handling.mjs +152 -0
  29. package/examples/esm.mjs +21 -0
  30. package/examples/server.js +16 -0
  31. package/jsdoc.json +16 -0
  32. package/lib/Bundle.mjs +66 -0
  33. package/lib/Client.mjs +137 -22
  34. package/lib/Message.mjs +87 -1
  35. package/lib/Server.mjs +117 -6
  36. package/lib/index.mjs +1 -0
  37. package/lib/internal/decode.mjs +4 -4
  38. package/{dist/lib/internal/osc.js → lib/osc.mjs} +71 -7
  39. package/package.json +12 -10
  40. package/rollup.config.mjs +48 -37
  41. package/scripts/generate-docs.mjs +229 -0
  42. package/test/fixtures/types/test-cjs-types.ts +19 -0
  43. package/test/fixtures/types/test-esm-types.ts +35 -0
  44. package/test/fixtures/types/tsconfig-cjs.test.json +17 -0
  45. package/test/fixtures/types/tsconfig-esm.test.json +17 -0
  46. package/test/test-bundle.mjs +0 -1
  47. package/test/test-client.mjs +152 -0
  48. package/test/test-e2e.mjs +9 -3
  49. package/test/test-encode-decode.mjs +847 -0
  50. package/test/test-error-handling.mjs +115 -0
  51. package/test/test-osc-internal.mjs +400 -42
  52. package/test/test-promises.mjs +249 -0
  53. package/test/test-types.mjs +39 -0
  54. package/test/util.mjs +15 -8
  55. package/tsconfig.json +45 -0
  56. package/types/Bundle.d.mts +70 -0
  57. package/types/Bundle.d.mts.map +1 -0
  58. package/types/Client.d.mts +101 -0
  59. package/types/Client.d.mts.map +1 -0
  60. package/types/Message.d.mts +84 -0
  61. package/types/Message.d.mts.map +1 -0
  62. package/types/Server.d.mts +98 -0
  63. package/types/Server.d.mts.map +1 -0
  64. package/types/index.d.mts +6 -0
  65. package/types/index.d.mts.map +1 -0
  66. package/types/internal/decode.d.mts +4 -0
  67. package/types/internal/decode.d.mts.map +1 -0
  68. package/types/osc.d.mts +66 -0
  69. package/types/osc.d.mts.map +1 -0
package/rollup.config.mjs CHANGED
@@ -20,48 +20,59 @@ function walk(root, result=[]) {
20
20
  }
21
21
 
22
22
  function walkLib(config) {
23
+ // Build all lib files in a single pass
23
24
  const files = walk('./lib/');
24
- files.forEach(({input, dir}) => {
25
- config.push({
26
- input,
27
- output: {
28
- entryFileNames: '[name].js',
29
- dir,
30
- format: 'cjs',
31
- preserveModules: true,
32
- exports: 'auto'
33
- },
34
- external: [
35
- 'node:dgram',
36
- 'node:events',
37
- 'jspack',
38
- '#decode'
39
- ]
40
- });
25
+ config.push({
26
+ input: files.map(f => f.input),
27
+ output: {
28
+ entryFileNames: '[name].js',
29
+ dir: 'dist/lib',
30
+ format: 'cjs',
31
+ preserveModules: true,
32
+ preserveModulesRoot: 'lib',
33
+ exports: 'auto'
34
+ },
35
+ external: [
36
+ 'node:dgram',
37
+ 'node:events',
38
+ 'node:buffer',
39
+ 'jspack',
40
+ '#decode'
41
+ ]
41
42
  });
42
43
  }
43
44
 
44
45
  function walkTest(config) {
45
- const tests = walk('./test/');
46
- tests.forEach(({input, dir}) => {
47
- config.push({
48
- input,
49
- plugins: [],
50
- output: {
51
- entryFileNames: '[name].js',
52
- dir,
53
- format: 'cjs',
54
- exports: 'auto',
55
- preserveModules: true
56
- },
57
- external: [
58
- 'node:dgram',
59
- 'node:net',
60
- 'node-osc',
61
- 'tap',
62
- '#decode'
63
- ]
64
- });
46
+ // Build all test files in a single pass, excluding fixtures
47
+ const tests = walk('./test/').filter(t => {
48
+ // Normalize path separators to work on both Unix and Windows
49
+ const normalizedPath = t.input.replace(/\\/g, '/');
50
+ return !normalizedPath.includes('/fixtures/');
51
+ });
52
+ config.push({
53
+ input: tests.map(t => t.input),
54
+ plugins: [],
55
+ output: {
56
+ entryFileNames: '[name].js',
57
+ dir: 'dist/test',
58
+ format: 'cjs',
59
+ exports: 'auto',
60
+ preserveModules: true,
61
+ preserveModulesRoot: 'test'
62
+ },
63
+ external: [
64
+ 'node:dgram',
65
+ 'node:net',
66
+ 'node:buffer',
67
+ 'node:events',
68
+ 'node:child_process',
69
+ 'node:fs',
70
+ 'node:path',
71
+ 'node:url',
72
+ 'node-osc',
73
+ 'tap',
74
+ '#decode'
75
+ ]
65
76
  });
66
77
  }
67
78
 
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Converts JSDoc JSON output to Markdown documentation.
5
+ * This script reads JSDoc JSON data and generates a formatted Markdown file.
6
+ */
7
+
8
+ import { writeFileSync } from 'node:fs';
9
+ import { execSync } from 'node:child_process';
10
+
11
+ // Generate JSDoc JSON
12
+ let jsdocJson;
13
+ try {
14
+ jsdocJson = execSync('npx jsdoc -X -c jsdoc.json', {
15
+ encoding: 'utf8',
16
+ maxBuffer: 10 * 1024 * 1024
17
+ });
18
+ } catch (error) {
19
+ console.error('❌ Failed to run JSDoc:');
20
+ console.error(error.message);
21
+ process.exit(1);
22
+ }
23
+
24
+ let docs;
25
+ try {
26
+ docs = JSON.parse(jsdocJson);
27
+ } catch (error) {
28
+ console.error('❌ Failed to parse JSDoc JSON output:');
29
+ console.error(error.message);
30
+ process.exit(1);
31
+ }
32
+
33
+ // Filter and organize documentation
34
+ const classes = {};
35
+ const functions = {};
36
+
37
+ docs.forEach(item => {
38
+ if (item.undocumented || item.ignore) return;
39
+
40
+ if (item.kind === 'class' && item.classdesc) {
41
+ if (!classes[item.name]) {
42
+ classes[item.name] = {
43
+ desc: item.classdesc,
44
+ constructor: null,
45
+ methods: [],
46
+ examples: item.examples || [],
47
+ augments: item.augments || []
48
+ };
49
+ }
50
+ // Look for constructor params
51
+ if (item.params) {
52
+ classes[item.name].constructor = {
53
+ params: item.params,
54
+ examples: item.examples || []
55
+ };
56
+ }
57
+ } else if (item.kind === 'function' && item.memberof) {
58
+ // Method of a class
59
+ const className = item.memberof;
60
+ if (!classes[className]) {
61
+ classes[className] = {
62
+ desc: '',
63
+ constructor: null,
64
+ methods: [],
65
+ examples: []
66
+ };
67
+ }
68
+ classes[className].methods.push(item);
69
+ } else if (item.kind === 'function' && !item.memberof && item.scope === 'global') {
70
+ // Top-level function
71
+ functions[item.name] = item;
72
+ }
73
+ });
74
+
75
+ // Generate Markdown
76
+ let markdown = `<!-- Generated by JSDoc. Update this documentation by updating the source code. -->
77
+
78
+ # API Reference
79
+
80
+ > **⚠️ This file is auto-generated from JSDoc comments in the source code.**
81
+ > To update this documentation, edit the JSDoc comments in the source files and run \`npm run docs\`.
82
+
83
+ This document provides detailed API reference for all classes, methods, and functions in node-osc.
84
+
85
+ For usage guides, best practices, and troubleshooting, see the **[Guide](./GUIDE.md)**.
86
+
87
+ ## Table of Contents
88
+
89
+ `;
90
+
91
+ // Define order: Server → Client → Message → Bundle → Low Level
92
+ const classOrder = ['Server', 'Client', 'Message', 'Bundle'];
93
+ const functionOrder = ['encode', 'decode'];
94
+
95
+ // Add classes to TOC
96
+ classOrder.forEach(name => {
97
+ if (classes[name]) {
98
+ markdown += `- [${name}](#${name.toLowerCase()})\n`;
99
+ if (classes[name].constructor) {
100
+ markdown += ` - [Constructor](#${name.toLowerCase()}-constructor)\n`;
101
+ }
102
+ classes[name].methods.forEach(method => {
103
+ markdown += ` - [${method.name}()](#${name.toLowerCase()}-${method.name.toLowerCase()})\n`;
104
+ });
105
+ }
106
+ });
107
+
108
+ // Add functions to TOC
109
+ markdown += `- [Low Level Functions](#low-level-functions)\n`;
110
+ functionOrder.forEach(name => {
111
+ if (functions[name]) {
112
+ markdown += ` - [${name}()](#${name.toLowerCase()})\n`;
113
+ }
114
+ });
115
+
116
+ markdown += `\n---\n\n`;
117
+
118
+ // Helper function to format parameters
119
+ function formatParams(params) {
120
+ if (!params || params.length === 0) return '';
121
+
122
+ let result = '\n**Parameters:**\n\n';
123
+ params.forEach(param => {
124
+ const optional = param.optional ? ' (optional)' : '';
125
+ const defaultVal = param.defaultvalue ? ` - Default: \`${param.defaultvalue}\`` : '';
126
+ const types = param.type ? param.type.names.join(' | ') : 'any';
127
+ result += `- \`${param.name}\` *{${types}}*${optional}${defaultVal} - ${param.description || ''}\n`;
128
+ });
129
+ return result;
130
+ }
131
+
132
+ // Helper function to format examples
133
+ function formatExamples(examples) {
134
+ if (!examples || examples.length === 0) return '';
135
+
136
+ let result = '\n**Examples:**\n\n';
137
+ examples.forEach(example => {
138
+ result += '```javascript\n' + example + '\n```\n\n';
139
+ });
140
+ return result;
141
+ }
142
+
143
+ // Helper function to format returns
144
+ function formatReturns(returns) {
145
+ if (!returns || returns.length === 0) return '';
146
+
147
+ const ret = returns[0];
148
+ const types = ret.type ? ret.type.names.join(' | ') : 'any';
149
+ return `\n**Returns:** *{${types}}* - ${ret.description || ''}\n`;
150
+ }
151
+
152
+ // Helper function to format throws
153
+ function formatThrows(exceptions) {
154
+ if (!exceptions || exceptions.length === 0) return '';
155
+
156
+ let result = '\n**Throws:**\n\n';
157
+ exceptions.forEach(ex => {
158
+ const types = ex.type ? ex.type.names.join(' | ') : 'Error';
159
+ result += `- *{${types}}* - ${ex.description || ''}\n`;
160
+ });
161
+ return result;
162
+ }
163
+
164
+ // Generate class documentation
165
+ classOrder.forEach(className => {
166
+ const classInfo = classes[className];
167
+ if (!classInfo) return;
168
+
169
+ markdown += `## ${className}\n\n`;
170
+
171
+ // Add extends info
172
+ if (classInfo.augments && classInfo.augments.length > 0) {
173
+ markdown += `**Extends:** ${classInfo.augments.join(', ')}\n\n`;
174
+ }
175
+
176
+ markdown += `${classInfo.desc}\n`;
177
+
178
+ // Class-level examples
179
+ if (classInfo.examples.length > 0 && !classInfo.constructor) {
180
+ markdown += formatExamples(classInfo.examples);
181
+ }
182
+
183
+ // Constructor
184
+ if (classInfo.constructor) {
185
+ markdown += `\n### ${className} Constructor\n\n`;
186
+ markdown += `Creates a new ${className} instance.\n`;
187
+ markdown += formatParams(classInfo.constructor.params);
188
+ markdown += formatExamples(classInfo.constructor.examples);
189
+ }
190
+
191
+ // Methods
192
+ classInfo.methods.forEach(method => {
193
+ markdown += `\n### ${className}.${method.name}()\n\n`;
194
+ markdown += `${method.description || ''}\n`;
195
+ markdown += formatParams(method.params);
196
+ markdown += formatReturns(method.returns);
197
+ markdown += formatThrows(method.exceptions);
198
+ markdown += formatExamples(method.examples);
199
+ });
200
+
201
+ markdown += `\n---\n\n`;
202
+ });
203
+
204
+ // Generate function documentation
205
+ markdown += `## Low Level Functions\n\n`;
206
+ markdown += `These functions provide low-level access to OSC encoding and decoding for advanced use cases.\n\n`;
207
+
208
+ functionOrder.forEach(funcName => {
209
+ const func = functions[funcName];
210
+ if (!func) return;
211
+
212
+ markdown += `### ${funcName}()\n\n`;
213
+ markdown += `${func.description || ''}\n`;
214
+ markdown += formatParams(func.params);
215
+ markdown += formatReturns(func.returns);
216
+ markdown += formatThrows(func.exceptions);
217
+ markdown += formatExamples(func.examples);
218
+ markdown += `\n`;
219
+ });
220
+
221
+ // Write output
222
+ try {
223
+ writeFileSync('docs/API.md', markdown, 'utf8');
224
+ console.log('✅ API documentation generated: docs/API.md');
225
+ } catch (error) {
226
+ console.error('❌ Failed to write API.md:');
227
+ console.error(error.message);
228
+ process.exit(1);
229
+ }
@@ -0,0 +1,19 @@
1
+ // Test CJS-style TypeScript imports
2
+ import type { Client, Server, Message, Bundle } from 'node-osc';
3
+ const osc = require('node-osc');
4
+
5
+ // Create server first (typical usage pattern)
6
+ const server: Server = new osc.Server(3333, '0.0.0.0');
7
+
8
+ // Create client after server
9
+ const client: Client = new osc.Client('127.0.0.1', 3333);
10
+
11
+ // Test Message type
12
+ const message: Message = new osc.Message('/test', 1, 2, 3);
13
+
14
+ // Test Bundle type
15
+ const bundle: Bundle = new osc.Bundle(['/one', 1]);
16
+
17
+ // Test encode/decode with consistent type annotations
18
+ const encoded: Buffer = osc.encode(message);
19
+ const decoded: Object = osc.decode(encoded);
@@ -0,0 +1,35 @@
1
+ // Test ESM TypeScript imports with Top-Level Await
2
+ import { once } from 'node:events';
3
+ import { Client, Server, Message, Bundle, encode, decode } from 'node-osc';
4
+
5
+ // Create server first (typical usage pattern)
6
+ const server: Server = new Server(3333, '0.0.0.0');
7
+
8
+ // Wait for server to be ready (pattern from examples)
9
+ await once(server, 'listening');
10
+
11
+ server.on('message', (msg) => {
12
+ console.log('Received message:', msg);
13
+ });
14
+
15
+ // Create client after server
16
+ const client: Client = new Client('127.0.0.1', 3333);
17
+
18
+ // Test async usage with Top-Level Await (ESM feature)
19
+ await client.send('/test', 1, 2, 3);
20
+ await client.close();
21
+ await server.close();
22
+
23
+ // Test Message type
24
+ const message: Message = new Message('/oscillator/frequency', 440);
25
+ message.append(3.14);
26
+ message.append('hello');
27
+ message.append(true);
28
+
29
+ // Test Bundle type
30
+ const bundle: Bundle = new Bundle(['/one', 1], ['/two', 2]);
31
+ bundle.append(['/three', 3]);
32
+
33
+ // Test encode/decode with consistent type annotations
34
+ const encoded: Buffer = encode(message);
35
+ const decoded: Object = decode(encoded);
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "noEmit": true,
4
+ "skipLibCheck": true,
5
+ "module": "commonjs",
6
+ "esModuleInterop": true,
7
+ "target": "ES2022",
8
+ "moduleResolution": "node",
9
+ "baseUrl": ".",
10
+ "paths": {
11
+ "node-osc": ["../../../types/index.d.mts"]
12
+ }
13
+ },
14
+ "include": [
15
+ "test-cjs-types.ts"
16
+ ]
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "noEmit": true,
4
+ "skipLibCheck": true,
5
+ "esModuleInterop": true,
6
+ "module": "ES2022",
7
+ "target": "ES2022",
8
+ "moduleResolution": "node",
9
+ "baseUrl": ".",
10
+ "paths": {
11
+ "node-osc": ["../../../types/index.d.mts"]
12
+ }
13
+ },
14
+ "include": [
15
+ "test-esm-types.ts"
16
+ ]
17
+ }
@@ -86,4 +86,3 @@ test('bundle: nested bundle', (t) => {
86
86
 
87
87
  client.send(payload);
88
88
  });
89
-
@@ -106,3 +106,155 @@ test('client: failure', (t) => {
106
106
  t.equal(err.code, 'ERR_SOCKET_DGRAM_NOT_RUNNING');
107
107
  });
108
108
  });
109
+
110
+ test('client: close with callback', (t) => {
111
+ const client = new Client('127.0.0.1', t.context.port);
112
+
113
+ t.plan(1);
114
+
115
+ client.close((err) => {
116
+ t.error(err, 'close should not error');
117
+ });
118
+ });
119
+
120
+ test('client: send bundle with non-numeric timetag', (t) => {
121
+ const oscServer = new Server(t.context.port, '127.0.0.1');
122
+ const client = new Client('127.0.0.1', t.context.port);
123
+
124
+ t.plan(2);
125
+
126
+ oscServer.on('bundle', (bundle) => {
127
+ oscServer.close();
128
+ t.equal(bundle.timetag, 0, 'should receive immediate execution timetag as 0');
129
+ t.ok(bundle.elements.length > 0, 'should have elements');
130
+ client.close();
131
+ });
132
+
133
+ // Send bundle with non-numeric timetag (will be encoded as immediate execution)
134
+ const bundle = {
135
+ oscType: 'bundle',
136
+ timetag: 'immediate', // Non-numeric, will trigger the else branch in writeTimeTag
137
+ elements: [
138
+ {
139
+ oscType: 'message',
140
+ address: '/test1',
141
+ args: [{ type: 'i', value: 42 }]
142
+ }
143
+ ]
144
+ };
145
+
146
+ client.send(bundle);
147
+ });
148
+
149
+ test('client: send bundle with null timetag', (t) => {
150
+ const oscServer = new Server(t.context.port, '127.0.0.1');
151
+ const client = new Client('127.0.0.1', t.context.port);
152
+
153
+ t.plan(2);
154
+
155
+ oscServer.on('bundle', (bundle) => {
156
+ oscServer.close();
157
+ t.equal(bundle.timetag, 0, 'should receive immediate execution timetag as 0');
158
+ t.ok(bundle.elements.length > 0, 'should have elements');
159
+ client.close();
160
+ });
161
+
162
+ // Send bundle with null timetag (will be encoded as immediate execution)
163
+ const bundle = {
164
+ oscType: 'bundle',
165
+ timetag: null, // Null, will trigger the else branch in writeTimeTag
166
+ elements: [
167
+ {
168
+ oscType: 'message',
169
+ address: '/test2',
170
+ args: [{ type: 's', value: 'hello' }]
171
+ }
172
+ ]
173
+ };
174
+
175
+ client.send(bundle);
176
+ });
177
+
178
+ test('client: send message with float type arg', (t) => {
179
+ const oscServer = new Server(t.context.port, '127.0.0.1');
180
+ const client = new Client('127.0.0.1', t.context.port);
181
+
182
+ t.plan(2);
183
+
184
+ oscServer.on('message', (msg) => {
185
+ oscServer.close();
186
+ t.equal(msg[0], '/float-test', 'should receive address');
187
+ t.ok(Math.abs(msg[1] - 9.876) < 0.001, 'should receive float value');
188
+ client.close();
189
+ });
190
+
191
+ // Send raw message with 'float' type to hit that case label
192
+ client.send({
193
+ oscType: 'message',
194
+ address: '/float-test',
195
+ args: [{ type: 'float', value: 9.876 }]
196
+ });
197
+ });
198
+
199
+ test('client: send message with blob type arg', (t) => {
200
+ const oscServer = new Server(t.context.port, '127.0.0.1');
201
+ const client = new Client('127.0.0.1', t.context.port);
202
+
203
+ t.plan(2);
204
+
205
+ oscServer.on('message', (msg) => {
206
+ oscServer.close();
207
+ t.equal(msg[0], '/blob-test', 'should receive address');
208
+ t.ok(Buffer.isBuffer(msg[1]), 'should receive blob as buffer');
209
+ client.close();
210
+ });
211
+
212
+ // Send raw message with 'blob' type to hit that case label
213
+ client.send({
214
+ oscType: 'message',
215
+ address: '/blob-test',
216
+ args: [{ type: 'blob', value: Buffer.from([0xAA, 0xBB]) }]
217
+ });
218
+ });
219
+
220
+ test('client: send message with double type arg', (t) => {
221
+ const oscServer = new Server(t.context.port, '127.0.0.1');
222
+ const client = new Client('127.0.0.1', t.context.port);
223
+
224
+ t.plan(2);
225
+
226
+ oscServer.on('message', (msg) => {
227
+ oscServer.close();
228
+ t.equal(msg[0], '/double-test', 'should receive address');
229
+ t.ok(Math.abs(msg[1] - 1.23456789) < 0.001, 'should receive double value as float');
230
+ client.close();
231
+ });
232
+
233
+ // Send raw message with 'double' type to hit that case label
234
+ client.send({
235
+ oscType: 'message',
236
+ address: '/double-test',
237
+ args: [{ type: 'double', value: 1.23456789 }]
238
+ });
239
+ });
240
+
241
+ test('client: send message with midi type arg', (t) => {
242
+ const oscServer = new Server(t.context.port, '127.0.0.1');
243
+ const client = new Client('127.0.0.1', t.context.port);
244
+
245
+ t.plan(2);
246
+
247
+ oscServer.on('message', (msg) => {
248
+ oscServer.close();
249
+ t.equal(msg[0], '/midi-test', 'should receive address');
250
+ t.ok(Buffer.isBuffer(msg[1]), 'should receive MIDI as buffer');
251
+ client.close();
252
+ });
253
+
254
+ // Send raw message with 'midi' type to hit that case label
255
+ client.send({
256
+ oscType: 'message',
257
+ address: '/midi-test',
258
+ args: [{ type: 'midi', value: Buffer.from([0x00, 0x90, 0x40, 0x60]) }]
259
+ });
260
+ });
package/test/test-e2e.mjs CHANGED
@@ -22,9 +22,12 @@ test('osc: argument message no callback', (t) => {
22
22
 
23
23
  t.plan(1);
24
24
 
25
- oscServer.on('message', (msg) => {
25
+ t.teardown(() => {
26
26
  oscServer.close();
27
27
  client.close();
28
+ });
29
+
30
+ oscServer.on('message', (msg) => {
28
31
  t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
29
32
  });
30
33
 
@@ -39,13 +42,16 @@ test('osc: client with callback and message as arguments', (t) => {
39
42
 
40
43
  t.plan(2);
41
44
 
42
- oscServer.on('message', (msg) => {
45
+ t.teardown(() => {
43
46
  oscServer.close();
47
+ client.close();
48
+ });
49
+
50
+ oscServer.on('message', (msg) => {
44
51
  t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
45
52
  });
46
53
 
47
54
  client.send('/test', 1, 2, 'testing', (err) => {
48
55
  t.error(err, 'there should be no error');
49
- client.close();
50
56
  });
51
57
  });