@tak-ps/node-tak 12.8.0 → 12.10.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.
@@ -1,46 +1,43 @@
1
- import test from 'tape';
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
2
3
  import { Queue } from '../lib/utils/queue.js';
3
- test('Queue - push/pop FIFO order', (t) => {
4
+ test('Queue - push/pop FIFO order', () => {
4
5
  const q = new Queue(4);
5
6
  q.push(1);
6
7
  q.push(2);
7
8
  q.push(3);
8
- t.equals(q.pop(), 1);
9
- t.equals(q.pop(), 2);
10
- t.equals(q.pop(), 3);
11
- t.end();
9
+ assert.equal(q.pop(), 1);
10
+ assert.equal(q.pop(), 2);
11
+ assert.equal(q.pop(), 3);
12
12
  });
13
- test('Queue - capacity enforced', (t) => {
13
+ test('Queue - capacity enforced', () => {
14
14
  const q = new Queue(2);
15
- t.equals(q.push(1), true);
16
- t.equals(q.push(2), true);
17
- t.equals(q.isFull, true);
18
- t.equals(q.push(3), false);
19
- t.equals(q.length, 2);
20
- t.end();
15
+ assert.equal(q.push(1), true);
16
+ assert.equal(q.push(2), true);
17
+ assert.equal(q.isFull, true);
18
+ assert.equal(q.push(3), false);
19
+ assert.equal(q.length, 2);
21
20
  });
22
- test('Queue - pop from empty', (t) => {
21
+ test('Queue - pop from empty', () => {
23
22
  const q = new Queue(2);
24
- t.equals(q.pop(), undefined);
25
- t.equals(q.length, 0);
26
- t.end();
23
+ assert.equal(q.pop(), undefined);
24
+ assert.equal(q.length, 0);
27
25
  });
28
- test('Queue - ring buffer wraparound', (t) => {
26
+ test('Queue - ring buffer wraparound', () => {
29
27
  const q = new Queue(3);
30
28
  q.push(1);
31
29
  q.push(2);
32
30
  q.push(3);
33
31
  // Pop two — head advances past midpoint
34
- t.equals(q.pop(), 1);
35
- t.equals(q.pop(), 2);
32
+ assert.equal(q.pop(), 1);
33
+ assert.equal(q.pop(), 2);
36
34
  // Push two more — tail wraps around
37
35
  q.push(4);
38
36
  q.push(5);
39
37
  // Remaining items come out in FIFO order
40
- t.equals(q.pop(), 3);
41
- t.equals(q.pop(), 4);
42
- t.equals(q.pop(), 5);
43
- t.equals(q.length, 0);
44
- t.end();
38
+ assert.equal(q.pop(), 3);
39
+ assert.equal(q.pop(), 4);
40
+ assert.equal(q.pop(), 5);
41
+ assert.equal(q.length, 0);
45
42
  });
46
43
  //# sourceMappingURL=queue.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"queue.test.js","sourceRoot":"","sources":["../../test/queue.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"queue.test.js","sourceRoot":"","sources":["../../test/queue.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAE9C,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE;IACrC,MAAM,CAAC,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACnC,MAAM,CAAC,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE;IAChC,MAAM,CAAC,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;IACxC,MAAM,CAAC,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,wCAAwC;IACxC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,oCAAoC;IACpC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,yCAAyC;IACzC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { Readable } from 'node:stream';
4
+ import { Client } from 'undici';
5
+ import TAKAPI from '../lib/api.js';
6
+ import { APIAuthCertificate } from '../lib/auth.js';
7
+ import stream2buffer from '../lib/stream.js';
8
+ test('Files.uploadPackage serializes multipart for certificate auth', async () => {
9
+ const originalRequest = Client.prototype.request;
10
+ let captured;
11
+ Client.prototype.request = async function (opts) {
12
+ captured = opts;
13
+ return {
14
+ statusCode: 200,
15
+ headers: {
16
+ 'content-type': 'text/plain'
17
+ },
18
+ body: Readable.from([Buffer.from('ok')]),
19
+ trailers: {}
20
+ };
21
+ };
22
+ try {
23
+ const api = new TAKAPI(new URL('https://tak.example.com'), new APIAuthCertificate('cert', 'key'));
24
+ const res = await api.Files.uploadPackage({
25
+ name: 'mission.zip',
26
+ creatorUid: 'user-1',
27
+ hash: 'hash-123',
28
+ keywords: ['alpha'],
29
+ groups: ['Blue']
30
+ }, Buffer.from('zip-bytes'));
31
+ assert.equal(res, 'ok');
32
+ assert.ok(captured);
33
+ assert.equal(captured.path, '/Marti/sync/missionupload?filename=mission.zip&creatorUid=user-1&hash=hash-123&keyword=missionpackage&keyword=alpha&Groups=Blue');
34
+ const headers = captured.headers;
35
+ assert.equal(typeof headers['Content-Type'], 'string');
36
+ assert.equal(typeof headers['Content-Length'], 'string');
37
+ assert.match(headers['Content-Type'], /^multipart\/form-data; boundary=----node-tak-/);
38
+ assert.equal(Number(headers['Content-Length']) > 0, true);
39
+ assert.ok(captured.body instanceof Readable);
40
+ const body = await stream2buffer(captured.body);
41
+ const multipart = body.toString('utf8');
42
+ assert.match(multipart, /Content-Disposition: form-data; name="assetfile"; filename="mission.zip"/);
43
+ assert.match(multipart, /Content-Type: application\/zip/);
44
+ assert.match(multipart, /zip-bytes/);
45
+ assert.match(multipart, /--$/m);
46
+ }
47
+ finally {
48
+ Client.prototype.request = originalRequest;
49
+ }
50
+ });
51
+ //# sourceMappingURL=uploadPackage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadPackage.test.js","sourceRoot":"","sources":["../../test/uploadPackage.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,MAAM,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAI7C,IAAI,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;IAC7E,MAAM,eAAe,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC;IAEjD,IAAI,QAAiC,CAAC;IAEtC,MAAM,CAAC,SAAS,CAAC,OAAO,GAAG,KAAK,WAAU,IAAiB;QACvD,QAAQ,GAAG,IAAI,CAAC;QAEhB,OAAO;YACH,UAAU,EAAE,GAAG;YACf,OAAO,EAAE;gBACL,cAAc,EAAE,YAAY;aAC/B;YACD,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACxC,QAAQ,EAAE,EAAE;SACuB,CAAC;IAC5C,CAAC,CAAC;IAEF,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,yBAAyB,CAAC,EAAE,IAAI,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAElG,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC;YACtC,IAAI,EAAE,aAAa;YACnB,UAAU,EAAE,QAAQ;YACpB,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,CAAC,OAAO,CAAC;YACnB,MAAM,EAAE,CAAC,MAAM,CAAC;SACnB,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;QAE7B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;QACpB,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,iIAAiI,CAAC,CAAC;QAC/J,MAAM,OAAO,GAAG,QAAQ,CAAC,OAA8B,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,CAAC;QACzD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAW,EAAE,+CAA+C,CAAC,CAAC;QACjG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAE1D,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,YAAY,QAAQ,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,0EAA0E,CAAC,CAAC;QACpG,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,gCAAgC,CAAC,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;YAAS,CAAC;QACP,MAAM,CAAC,SAAS,CAAC,OAAO,GAAG,eAAe,CAAC;IAC/C,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"root":["../cli.ts","../index.ts","../test/default.test.ts","../test/findCoT.test.ts","../test/pipeline.test.ts","../test/queue.test.ts","../lib/api.ts","../lib/auth.ts","../lib/commands.ts","../lib/fetch.ts","../lib/stream.ts","../lib/api/certificate.ts","../lib/api/client.ts","../lib/api/contacts.ts","../lib/api/credentials.ts","../lib/api/export.ts","../lib/api/files.ts","../lib/api/groups.ts","../lib/api/iconsets.ts","../lib/api/injectors.ts","../lib/api/locate.ts","../lib/api/mission-invite.ts","../lib/api/mission-layer.ts","../lib/api/mission-log.ts","../lib/api/mission.ts","../lib/api/oauth.ts","../lib/api/package.ts","../lib/api/profile.ts","../lib/api/query.ts","../lib/api/repeater.ts","../lib/api/security.ts","../lib/api/subscriptions.ts","../lib/api/types.ts","../lib/api/video.ts","../lib/utils/queue.ts"],"version":"6.0.2"}
1
+ {"root":["../cli.ts","../index.ts","../test/default.test.ts","../test/findCoT.test.ts","../test/pipeline.test.ts","../test/queue.test.ts","../test/uploadPackage.test.ts","../lib/api.ts","../lib/auth.ts","../lib/commands.ts","../lib/fetch.ts","../lib/multipart.ts","../lib/stream.ts","../lib/api/certificate.ts","../lib/api/client.ts","../lib/api/contacts.ts","../lib/api/credentials.ts","../lib/api/export.ts","../lib/api/files.ts","../lib/api/groups.ts","../lib/api/iconsets.ts","../lib/api/injectors.ts","../lib/api/locate.ts","../lib/api/mission-invite.ts","../lib/api/mission-layer.ts","../lib/api/mission-log.ts","../lib/api/mission.ts","../lib/api/oauth.ts","../lib/api/package.ts","../lib/api/profile.ts","../lib/api/query.ts","../lib/api/repeater.ts","../lib/api/security.ts","../lib/api/subscriptions.ts","../lib/api/types.ts","../lib/api/video.ts","../lib/utils/queue.ts"],"version":"6.0.2"}
package/lib/auth.ts CHANGED
@@ -3,6 +3,7 @@ import { Type } from '@sinclair/typebox';
3
3
  import { Client } from 'undici';
4
4
  import TAKAPI from './api.js';
5
5
  import stream2buffer from './stream.js';
6
+ import { encodeMultipartFormData } from './multipart.js';
6
7
 
7
8
  /**
8
9
  * Store the TAK Client Certificate for a connection
@@ -97,6 +98,18 @@ export class APIAuthCertificate extends APIAuth {
97
98
  }
98
99
  });
99
100
 
101
+ if (typeof FormData !== 'undefined' && opts.body instanceof FormData) {
102
+ const multipart = await encodeMultipartFormData(opts.body);
103
+ opts = {
104
+ ...opts,
105
+ headers: {
106
+ ...(opts.headers || {}),
107
+ ...multipart.headers
108
+ },
109
+ body: multipart.body
110
+ };
111
+ }
112
+
100
113
  const res = await client.request({
101
114
  path: String(url).replace(api.url.origin, ''),
102
115
  ...opts
@@ -0,0 +1,99 @@
1
+ import crypto from 'node:crypto';
2
+ import { Readable } from 'node:stream';
3
+
4
+ type MultipartBufferPart = {
5
+ header: Buffer;
6
+ kind: 'buffer';
7
+ body: Buffer;
8
+ trailer: Buffer;
9
+ size: number;
10
+ };
11
+
12
+ type MultipartFilePart = {
13
+ header: Buffer;
14
+ kind: 'file';
15
+ body: File;
16
+ trailer: Buffer;
17
+ size: number;
18
+ };
19
+
20
+ type MultipartPart = MultipartBufferPart | MultipartFilePart;
21
+
22
+ function escapeQuoted(value: string): string {
23
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
24
+ }
25
+
26
+ export async function encodeMultipartFormData(form: FormData): Promise<{
27
+ body: Readable;
28
+ headers: {
29
+ 'Content-Type': string;
30
+ 'Content-Length': string;
31
+ };
32
+ }> {
33
+ const boundary = `----node-tak-${crypto.randomUUID()}`;
34
+ const closing = Buffer.from(`--${boundary}--\r\n`);
35
+
36
+ const parts: MultipartPart[] = Array.from(form.entries()).map(([name, value]) => {
37
+ if (typeof value === 'string') {
38
+ const header = Buffer.from(
39
+ `--${boundary}\r\n`
40
+ + `Content-Disposition: form-data; name="${escapeQuoted(name)}"\r\n\r\n`
41
+ );
42
+ const body = Buffer.from(value);
43
+ const trailer = Buffer.from('\r\n');
44
+
45
+ return {
46
+ header,
47
+ kind: 'buffer',
48
+ body,
49
+ trailer,
50
+ size: header.length + body.length + trailer.length,
51
+ };
52
+ }
53
+
54
+ const filename = 'name' in value && typeof value.name === 'string'
55
+ ? value.name
56
+ : 'blob';
57
+ const type = value.type || 'application/octet-stream';
58
+ const header = Buffer.from(
59
+ `--${boundary}\r\n`
60
+ + `Content-Disposition: form-data; name="${escapeQuoted(name)}"; filename="${escapeQuoted(filename)}"\r\n`
61
+ + `Content-Type: ${type}\r\n\r\n`
62
+ );
63
+ const trailer = Buffer.from('\r\n');
64
+
65
+ return {
66
+ header,
67
+ kind: 'file',
68
+ body: value,
69
+ trailer,
70
+ size: header.length + value.size + trailer.length,
71
+ };
72
+ });
73
+
74
+ const contentLength = parts.reduce((sum, part) => sum + part.size, 0) + closing.length;
75
+
76
+ const body = Readable.from((async function*() {
77
+ for (const part of parts) {
78
+ yield part.header;
79
+
80
+ if (part.kind === 'buffer') {
81
+ yield part.body;
82
+ } else {
83
+ yield Buffer.from(await part.body.arrayBuffer());
84
+ }
85
+
86
+ yield part.trailer;
87
+ }
88
+
89
+ yield closing;
90
+ })());
91
+
92
+ return {
93
+ body,
94
+ headers: {
95
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
96
+ 'Content-Length': String(contentLength),
97
+ }
98
+ };
99
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tak-ps/node-tak",
3
3
  "type": "module",
4
- "version": "12.8.0",
4
+ "version": "12.10.0",
5
5
  "description": "Lightweight JavaScript library for communicating with TAK Server",
6
6
  "author": "Nick Ingalls <nick@ingalls.ca>",
7
7
  "main": "dist/index.js",
@@ -44,10 +44,8 @@
44
44
  "@inquirer/prompts": "^8.0.1",
45
45
  "@types/node": "^25.0.0",
46
46
  "@types/pem": "^1.14.4",
47
- "@types/tape": "^5.6.0",
48
47
  "c8": "^11.0.0",
49
48
  "eslint": "^10.0.0",
50
- "tape": "^5.6.1",
51
49
  "tsx": "^4.19.4",
52
50
  "typedoc": "^0.28.1",
53
51
  "typescript": "^6.0.0",
@@ -1,9 +1,9 @@
1
1
  import TAK, { CoT } from '../index.js';
2
- import test from 'tape';
2
+ import test from 'node:test';
3
+ import assert from 'node:assert/strict';
3
4
 
4
- test('Ensure Export', (t) => {
5
- t.ok(TAK);
6
- t.ok(CoT);
7
- t.end();
5
+ test('Ensure Export', () => {
6
+ assert.ok(TAK);
7
+ assert.ok(CoT);
8
8
  });
9
9
 
@@ -1,22 +1,21 @@
1
1
  import TAK from '../index.js';
2
- import test from 'tape';
2
+ import test from 'node:test';
3
+ import assert from 'node:assert/strict';
3
4
 
4
- test('findCoT - Unfinished', (t) => {
5
+ test('findCoT - Unfinished', () => {
5
6
  const res = TAK.findCoT('<event ><detail>');
6
- t.equals(res, null);
7
- t.end();
7
+ assert.equal(res, null);
8
8
  });
9
9
 
10
- test('findCoT - Basic', (t) => {
10
+ test('findCoT - Basic', () => {
11
11
  const res = TAK.findCoT('<event></event>');
12
- t.deepEquals(res, {
12
+ assert.deepEqual(res, {
13
13
  event: '<event></event>',
14
14
  remainder: '',
15
15
  });
16
- t.end();
17
16
  });
18
17
 
19
- test('findCoT - New Lines', (t) => {
18
+ test('findCoT - New Lines', () => {
20
19
  const res = TAK.findCoT(`
21
20
  <event>
22
21
  <detail remarks="
@@ -24,14 +23,13 @@ I am a multiline
24
23
  remarks field
25
24
  "/>
26
25
  </event>`);
27
- t.deepEquals(res, {
26
+ assert.deepEqual(res, {
28
27
  event: '<event>\n <detail remarks="\nI am a multiline\nremarks field\n "/>\n</event>',
29
28
  remainder: '',
30
29
  });
31
- t.end();
32
30
  });
33
31
 
34
- test('findCoT - New Lines - Non-Greedy', (t) => {
32
+ test('findCoT - New Lines - Non-Greedy', () => {
35
33
  const res = TAK.findCoT(`
36
34
  <event>
37
35
  <detail remarks="
@@ -44,48 +42,44 @@ I am a multiline
44
42
  remarks field
45
43
  "/>
46
44
  </event>`);
47
- t.deepEquals(res, {
45
+ assert.deepEqual(res, {
48
46
  event: '<event>\n <detail remarks="\nI am a multiline\nremarks field\n "/>\n</event>',
49
47
  remainder: '<event>\n <detail remarks="\nI am a multiline\nremarks field\n "/>\n</event>',
50
48
  });
51
- t.end();
52
49
  });
53
50
 
54
- test('findCoT - bad preceding data', (t) => {
51
+ test('findCoT - bad preceding data', () => {
55
52
  const res = TAK.findCoT(`
56
53
  <fake/>
57
54
  <event><detail remarks="I am remarks"/>
58
55
  </event>
59
56
  `);
60
- t.deepEquals(res, {
57
+ assert.deepEqual(res, {
61
58
  event: '<event><detail remarks="I am remarks"/>\n</event>',
62
59
  remainder: '\n',
63
60
  });
64
- t.end();
65
61
  });
66
62
 
67
- test('findCoT - bad post data', (t) => {
63
+ test('findCoT - bad post data', () => {
68
64
  const res = TAK.findCoT(`
69
65
  <event><detail remarks="I am remarks"/>
70
66
  </event>
71
67
  <fake/>
72
68
  `);
73
- t.deepEquals(res, {
69
+ assert.deepEqual(res, {
74
70
  event: '<event><detail remarks="I am remarks"/>\n</event>',
75
71
  remainder: '\n<fake/>\n',
76
72
  });
77
- t.end();
78
73
  });
79
74
 
80
- test('findCoT - mixed', (t) => {
75
+ test('findCoT - mixed', () => {
81
76
  const res = TAK.findCoT(`
82
77
  <event><detail remarks="I am remarks"/>
83
78
  </event>
84
79
  <fake/>
85
80
  <event><detail remarks="I am remarks"/></event>`);
86
- t.deepEquals(res, {
81
+ assert.deepEqual(res, {
87
82
  event: '<event><detail remarks="I am remarks"/>\n</event>',
88
83
  remainder: '\n<fake/>\n<event><detail remarks="I am remarks"/></event>',
89
84
  });
90
- t.end();
91
85
  });