node-turbo 1.0.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.
- package/.c8rc.json +5 -0
- package/.esdoc.json +83 -0
- package/.eslintrc.json +10 -0
- package/.mocharc.json +7 -0
- package/LICENSE +21 -0
- package/README.md +620 -0
- package/docs/API.md +857 -0
- package/lib/errors/index.js +36 -0
- package/lib/express/express-turbo-stream.js +41 -0
- package/lib/express/index.js +4 -0
- package/lib/express/turbocharge-express.js +108 -0
- package/lib/index.js +8 -0
- package/lib/koa/index.js +4 -0
- package/lib/koa/koa-turbo-stream.js +44 -0
- package/lib/koa/turbocharge-koa.js +122 -0
- package/lib/request-helpers.js +53 -0
- package/lib/sse/index.js +3 -0
- package/lib/sse/sse-turbo-stream.js +137 -0
- package/lib/turbo-element.js +71 -0
- package/lib/turbo-frame.js +79 -0
- package/lib/turbo-readable.js +125 -0
- package/lib/turbo-stream-element.js +67 -0
- package/lib/turbo-stream.js +350 -0
- package/lib/ws/index.js +4 -0
- package/lib/ws/ws-turbo-stream.js +112 -0
- package/package.json +75 -0
- package/test/hooks.js +46 -0
- package/test/integration/express.test.js +137 -0
- package/test/integration/koa.test.js +125 -0
- package/test/integration/sse.test.js +80 -0
- package/test/integration/ws.test.js +155 -0
- package/test/package.test.js +68 -0
- package/test/unit/core/request-helpers.test.js +97 -0
- package/test/unit/core/turbo-element.test.js +15 -0
- package/test/unit/core/turbo-frame.test.js +63 -0
- package/test/unit/core/turbo-readable.test.js +93 -0
- package/test/unit/core/turbo-stream-element.test.js +76 -0
- package/test/unit/core/turbo-stream.test.js +308 -0
- package/test/unit/express/express-turbo-stream.test.js +39 -0
- package/test/unit/express/turbocharge-express.test.js +123 -0
- package/test/unit/koa/koa-turbo-stream.test.js +56 -0
- package/test/unit/koa/turbocharge-koa.test.js +141 -0
- package/test/unit/sse/sse-turbo-stream.test.js +109 -0
- package/test/unit/ws/ws-turbo-stream.test.js +46 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import request from 'supertest';
|
|
4
|
+
import { TurboStream } from '#core';
|
|
5
|
+
import { WsTurboStream } from '#ws';
|
|
6
|
+
import { WebSocketServer, WebSocket, createWebSocketStream } from 'ws';
|
|
7
|
+
|
|
8
|
+
// The port of our test server.
|
|
9
|
+
const port = 8888;
|
|
10
|
+
|
|
11
|
+
describe('WebSocket integration', function() {
|
|
12
|
+
|
|
13
|
+
// Create server.
|
|
14
|
+
before(function() {
|
|
15
|
+
this.wss = new WebSocketServer({ port });
|
|
16
|
+
|
|
17
|
+
this.wss.on('connection', ws => {
|
|
18
|
+
ws.on('error', expect.fail);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// Close all connections and the server.
|
|
24
|
+
after(function() {
|
|
25
|
+
this.wss.clients.forEach(ws => {
|
|
26
|
+
ws.close();
|
|
27
|
+
|
|
28
|
+
// A bit brutal but whatevs.
|
|
29
|
+
process.nextTick(() => {
|
|
30
|
+
if ([WebSocket.OPEN, WebSocket.CLOSING].includes(ws.readyState)) {
|
|
31
|
+
ws.terminate();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
this.wss.close();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
it('Client receives Turbo Stream message (buffer = true)', function() {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
// Create client.
|
|
43
|
+
const ws = new WebSocket(`ws://localhost:${port}`, { perMessageDeflate: false });
|
|
44
|
+
|
|
45
|
+
ws.on('error', err => {
|
|
46
|
+
return reject(err);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// We're expecting one message with two elements.
|
|
50
|
+
ws.on('message', data => {
|
|
51
|
+
expect(data.toString()).to.equal('<turbo-stream action="append" target="t1"><template>c1</template></turbo-stream>\n<turbo-stream action="update" target="t2"><template>c2</template></turbo-stream>');
|
|
52
|
+
ws.close();
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// As soon as the client is ready, the server broadcasts the Turbo Stream message.
|
|
57
|
+
ws.on('open', () => {
|
|
58
|
+
this.wss.clients.forEach(ws => {
|
|
59
|
+
const wsts = new WsTurboStream(ws);
|
|
60
|
+
wsts
|
|
61
|
+
.append('t1', 'c1')
|
|
62
|
+
.update('t2', 'c2')
|
|
63
|
+
.flush();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
it('Client receives Turbo Stream message (buffer = false)', function() {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
// Create client.
|
|
73
|
+
const ws = new WebSocket(`ws://localhost:${port}`, { perMessageDeflate: false });
|
|
74
|
+
const messages = [];
|
|
75
|
+
|
|
76
|
+
ws.on('error', err => {
|
|
77
|
+
return reject(err);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// We're expecting one message with two elements.
|
|
81
|
+
ws.on('message', data => {
|
|
82
|
+
messages.push(data.toString());
|
|
83
|
+
if (messages.length === 2) {
|
|
84
|
+
expect(messages[0]).to.equal('<turbo-stream action="append" target="t1"><template>c1</template></turbo-stream>');
|
|
85
|
+
expect(messages[1]).to.equal('<turbo-stream action="update" target="t2"><template>c2</template></turbo-stream>');
|
|
86
|
+
ws.close();
|
|
87
|
+
resolve();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// As soon as the client is ready, the server broadcasts the Turbo Stream message.
|
|
92
|
+
ws.on('open', () => {
|
|
93
|
+
this.wss.clients.forEach(ws => {
|
|
94
|
+
WsTurboStream
|
|
95
|
+
.use(ws)
|
|
96
|
+
.append('t1', 'c1')
|
|
97
|
+
.update('t2', 'c2');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
it('Client receives Turbo Stream message through Node.js streams API', function() {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
// Create client.
|
|
107
|
+
const ws = new WebSocket(`ws://localhost:${port}`, { perMessageDeflate: false });
|
|
108
|
+
|
|
109
|
+
ws.on('error', err => {
|
|
110
|
+
return reject(err);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const messages = [];
|
|
114
|
+
|
|
115
|
+
// We're expecting one message with two elements.
|
|
116
|
+
ws.on('message', (data, isBinary) => {
|
|
117
|
+
messages.push(data.toString());
|
|
118
|
+
if (messages.length === 2) {
|
|
119
|
+
expect(messages[0]).to.equal('<turbo-stream action="append" target="t1"><template>c1</template></turbo-stream>');
|
|
120
|
+
expect(messages[1]).to.equal('<turbo-stream action="update" target="t2"><template>c2</template></turbo-stream>');
|
|
121
|
+
ws.close();
|
|
122
|
+
resolve();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// As soon as the client is ready, the server broadcasts the Turbo Stream message.
|
|
127
|
+
ws.once('open', () => {
|
|
128
|
+
this.wss.clients.forEach(ws => {
|
|
129
|
+
const ts = new TurboStream();
|
|
130
|
+
const readable = ts.createReadableStream();
|
|
131
|
+
|
|
132
|
+
readable.on('error', err => {
|
|
133
|
+
return reject(err);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const wsStream = createWebSocketStream(ws, { encoding: 'utf8' });
|
|
137
|
+
|
|
138
|
+
wsStream.on('error', err => {
|
|
139
|
+
return reject(err);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Pipe TurboReadable to the WS duplex stream.
|
|
143
|
+
readable.pipe(wsStream);
|
|
144
|
+
|
|
145
|
+
// Write to TurboReadable.
|
|
146
|
+
ts.append('t1', 'c1').update('t2', 'c2');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import * as core from 'node-turbo';
|
|
4
|
+
import * as ws from 'node-turbo/ws';
|
|
5
|
+
import * as koa from 'node-turbo/koa';
|
|
6
|
+
import * as express from 'node-turbo/express';
|
|
7
|
+
import * as sse from 'node-turbo/sse';
|
|
8
|
+
import * as errors from 'node-turbo/errors';
|
|
9
|
+
|
|
10
|
+
const importedModules = { core, ws, koa, express, sse, errors };
|
|
11
|
+
const expectedModules = {
|
|
12
|
+
core: [
|
|
13
|
+
'TurboFrame',
|
|
14
|
+
'TurboStream',
|
|
15
|
+
'TurboStreamElement',
|
|
16
|
+
'TurboElement',
|
|
17
|
+
'TurboReadable',
|
|
18
|
+
'isTurboFrameRequest',
|
|
19
|
+
'isTurboStreamRequest',
|
|
20
|
+
'getTurboFrameId'
|
|
21
|
+
],
|
|
22
|
+
ws: [
|
|
23
|
+
'WsTurboStream'
|
|
24
|
+
],
|
|
25
|
+
koa: [
|
|
26
|
+
'turbochargeKoa',
|
|
27
|
+
'KoaTurboStream'
|
|
28
|
+
],
|
|
29
|
+
express: [
|
|
30
|
+
'turbochargeExpress',
|
|
31
|
+
'ExpressTurboStream'
|
|
32
|
+
],
|
|
33
|
+
sse: [
|
|
34
|
+
'SseTurboStream'
|
|
35
|
+
],
|
|
36
|
+
errors: [
|
|
37
|
+
'ValidationError',
|
|
38
|
+
'AttributeMalformedError',
|
|
39
|
+
'AttributeMissingError',
|
|
40
|
+
'AttributeInvalidError'
|
|
41
|
+
]
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function sameMembers(arr1, arr2) {
|
|
45
|
+
const set1 = new Set(arr1);
|
|
46
|
+
const set2 = new Set(arr2);
|
|
47
|
+
return arr1.every(item => set2.has(item)) &&
|
|
48
|
+
arr2.every(item => set1.has(item))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('Package', function() {
|
|
52
|
+
|
|
53
|
+
Object.keys(expectedModules).forEach(modName => {
|
|
54
|
+
describe(modName, function() {
|
|
55
|
+
expectedModules[modName].forEach(name => {
|
|
56
|
+
it(`${name} should be exposed`, function() {
|
|
57
|
+
expect(importedModules[modName][name]).to.be.a('function');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it(`Nothing else should be exposed`, function() {
|
|
62
|
+
expect(sameMembers(Object.keys(importedModules[modName]), expectedModules[modName])).to.be.true;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import * as hlp from '../../../lib/request-helpers.js';
|
|
4
|
+
import httpMocks from 'node-mocks-http';
|
|
5
|
+
|
|
6
|
+
describe('Request helpers', function() {
|
|
7
|
+
|
|
8
|
+
describe('isTurboStreamRequest()', function() {
|
|
9
|
+
|
|
10
|
+
it('returns true on recognised MIME type', function() {
|
|
11
|
+
const req = httpMocks.createRequest({
|
|
12
|
+
method: 'POST',
|
|
13
|
+
url: '/',
|
|
14
|
+
headers: {
|
|
15
|
+
'Accept': 'text/html',
|
|
16
|
+
'Accept': 'text/vnd.turbo-stream.html'
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(hlp.isTurboStreamRequest(req)).to.be.true;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
it('returns false on missing MIME type', function() {
|
|
25
|
+
const req = httpMocks.createRequest({
|
|
26
|
+
method: 'POST',
|
|
27
|
+
url: '/',
|
|
28
|
+
headers: {
|
|
29
|
+
'Accept': 'text/html'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(hlp.isTurboStreamRequest(req)).to.be.false;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
it('returns false when missing headers fields', function() {
|
|
38
|
+
expect(hlp.isTurboStreamRequest({})).to.be.false;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('isTurboFrameRequest()', function() {
|
|
44
|
+
|
|
45
|
+
it('returns true on recognised turbo-frame', function() {
|
|
46
|
+
const req = httpMocks.createRequest({
|
|
47
|
+
method: 'POST',
|
|
48
|
+
url: '/',
|
|
49
|
+
headers: {
|
|
50
|
+
'turbo-frame': 'name',
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(hlp.isTurboFrameRequest(req)).to.be.true;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
it('returns false on missing turbo-frame', function() {
|
|
59
|
+
const req = httpMocks.createRequest({
|
|
60
|
+
method: 'POST',
|
|
61
|
+
url: '/',
|
|
62
|
+
headers: {
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(hlp.isTurboFrameRequest(req)).to.be.false;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns false when missing headers fields', function() {
|
|
70
|
+
expect(hlp.isTurboFrameRequest({})).to.be.false;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
describe('getTurboFrameId()', function() {
|
|
77
|
+
|
|
78
|
+
it('returns ID of turbo-frame if available', function() {
|
|
79
|
+
const req = httpMocks.createRequest({
|
|
80
|
+
method: 'POST',
|
|
81
|
+
url: '/',
|
|
82
|
+
headers: {
|
|
83
|
+
'turbo-frame': 'name',
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(hlp.getTurboFrameId(req)).to.equal('name');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns null in any other case', function() {
|
|
91
|
+
expect(hlp.getTurboFrameId({})).to.be.null;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { TurboElement } from '#core';
|
|
4
|
+
|
|
5
|
+
describe('TurboElement', function() {
|
|
6
|
+
|
|
7
|
+
it('Constructor correctly assigns properties (and throws Error)', function() {
|
|
8
|
+
expect(function() {
|
|
9
|
+
const el = new TurboElement({ foo: 'bar' }, 'c');
|
|
10
|
+
expect(el.content).to.equal('c');
|
|
11
|
+
expect(el.attributes).to.deep.equal({ foo: 'bar' });
|
|
12
|
+
}).to.throw();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { TurboFrame } from '#core';
|
|
4
|
+
import { AttributeMissingError, AttributeMalformedError } from '#errors';
|
|
5
|
+
|
|
6
|
+
describe('TurboFrame', function() {
|
|
7
|
+
|
|
8
|
+
it('Constructor with id string correctly assigns attributes', function() {
|
|
9
|
+
const tf = new TurboFrame('id', 'c');
|
|
10
|
+
expect(tf.attributes).to.deep.equal({ id: 'id' });
|
|
11
|
+
expect(tf.content).to.equal('c');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
it('Constructor with attribute object correctly assigns attributes', function() {
|
|
16
|
+
const tf = new TurboFrame({ id: 'id', foo: 'bar' }, 'c');
|
|
17
|
+
expect(tf.attributes).to.deep.equal({ id: 'id', foo: 'bar' });
|
|
18
|
+
expect(tf.content).to.equal('c');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
it('Error is thrown when missing attribute "id"', function() {
|
|
23
|
+
expect(function() {
|
|
24
|
+
const tf = new TurboFrame();
|
|
25
|
+
}).to.throw();
|
|
26
|
+
|
|
27
|
+
expect(function() {
|
|
28
|
+
const tf = new TurboFrame({});
|
|
29
|
+
}).to.throw();
|
|
30
|
+
|
|
31
|
+
expect(function() {
|
|
32
|
+
const tf = new TurboFrame({ id: null });
|
|
33
|
+
}).to.throw();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
it('Error is thrown when attribute "id" is malformed', function() {
|
|
38
|
+
expect(function() {
|
|
39
|
+
const tf = new TurboFrame(8);
|
|
40
|
+
}).to.throw();
|
|
41
|
+
|
|
42
|
+
expect(function() {
|
|
43
|
+
const tf = new TurboFrame({ id: 12 });
|
|
44
|
+
}).to.throw();
|
|
45
|
+
|
|
46
|
+
expect(function() {
|
|
47
|
+
const tf = new TurboFrame('');
|
|
48
|
+
}).to.throw();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
it('render() creates correct HTML', function() {
|
|
53
|
+
const tf = new TurboFrame('id', 'c');
|
|
54
|
+
expect(tf.render()).to.equal('<turbo-frame id="id">c</turbo-frame>');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
it('render() creates correct HTML (additional attributes)', function() {
|
|
59
|
+
const tf = new TurboFrame({ id: 'id', foo: 'bar' }, 'c', );
|
|
60
|
+
expect(tf.render()).to.equal('<turbo-frame id="id" foo="bar">c</turbo-frame>');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
|
|
2
|
+
import chai, { expect } from 'chai';
|
|
3
|
+
import spies from 'chai-spies';
|
|
4
|
+
import eventemitter2 from 'chai-eventemitter2';
|
|
5
|
+
import { TurboStream, TurboElement, TurboStreamElement, TurboReadable } from '#core';
|
|
6
|
+
import { Readable } from 'node:stream';
|
|
7
|
+
|
|
8
|
+
chai.use(eventemitter2());
|
|
9
|
+
chai.use(spies);
|
|
10
|
+
// const sandbox = chai.spy.sandbox();
|
|
11
|
+
|
|
12
|
+
const attr = {
|
|
13
|
+
action: 'a',
|
|
14
|
+
target: 't'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('TurboReadable', function() {
|
|
18
|
+
|
|
19
|
+
before(function() {
|
|
20
|
+
this.ts = new TurboStream()
|
|
21
|
+
this.readable = new TurboReadable(this.ts);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
beforeEach(function() {
|
|
26
|
+
chai.spy.on(this.readable, 'push');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
afterEach(function() {
|
|
31
|
+
chai.spy.restore(this.readable, 'push');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
it('new TurboReadable() pushes existing elements', function() {
|
|
36
|
+
const ts = new TurboStream()
|
|
37
|
+
.append('t', 'c')
|
|
38
|
+
.append('t2', 'c2');
|
|
39
|
+
const readable = new TurboReadable(ts);
|
|
40
|
+
const data = [];
|
|
41
|
+
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
readable.on('data', chunk => {
|
|
44
|
+
data.push(chunk);
|
|
45
|
+
|
|
46
|
+
if (data.length == 2) {
|
|
47
|
+
expect(data.length).to.equal(2);
|
|
48
|
+
readable.done();
|
|
49
|
+
resolve();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
readable.on('error', err => reject(err));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
it('new TurboReadable() throws error if argument not TurboStream', function() {
|
|
60
|
+
expect(function() {
|
|
61
|
+
const readable = new TurboReadable({ foo: 'bar' });
|
|
62
|
+
}).to.throw();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
it('_pushElement() calls push()', function() {
|
|
67
|
+
this.readable._pushElement(new TurboStreamElement({ action: 'a', target: 't' }));
|
|
68
|
+
expect(this.readable.push).to.have.been.called();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
it('_boundPush() is bound to _pushElement', function() {
|
|
73
|
+
expect(this.readable._boundPush.name).to.be.equal('bound _pushElement');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
it('_destroy() gets called when steam is destroyed', function() {
|
|
78
|
+
const readable = this.ts.createReadableStream();
|
|
79
|
+
chai.spy.on(readable, '_destroy');
|
|
80
|
+
readable.destroy();
|
|
81
|
+
|
|
82
|
+
expect(readable._destroy).to.have.been.called();
|
|
83
|
+
chai.spy.restore(readable, '_destroy');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
it('done() calls push(null)', function() {
|
|
88
|
+
this.readable.done();
|
|
89
|
+
|
|
90
|
+
expect(this.readable.push).to.have.been.called.with(null);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { TurboStreamElement } from '#core';
|
|
4
|
+
|
|
5
|
+
describe('TurboStreamElement', function() {
|
|
6
|
+
|
|
7
|
+
it('Error is thrown when missing attributes object', function() {
|
|
8
|
+
expect(function() {
|
|
9
|
+
const tse = new TurboStreamElement();
|
|
10
|
+
}).to.throw();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
it(`Error is thrown when missing attribute "action"`, function() {
|
|
15
|
+
expect(function() {
|
|
16
|
+
const tse = new TurboStreamElement({ target: 't' });
|
|
17
|
+
}).to.throw();
|
|
18
|
+
|
|
19
|
+
expect(function() {
|
|
20
|
+
const tse = new TurboStreamElement({ target: 't', action: null });
|
|
21
|
+
}).to.throw();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
it(`Error is thrown when attribute "action" is malformed`, function() {
|
|
26
|
+
expect(function() {
|
|
27
|
+
const tse = new TurboStreamElement({ target: 't', action: 8 });
|
|
28
|
+
}).to.throw();
|
|
29
|
+
|
|
30
|
+
expect(function() {
|
|
31
|
+
const tse = new TurboStreamElement({ target: 't', action: '' });
|
|
32
|
+
}).to.throw();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
it(`Error is thrown when missing attribute "target" or "targets"`, function() {
|
|
37
|
+
expect(function() {
|
|
38
|
+
const tse = new TurboStreamElement({ action: 'a' });
|
|
39
|
+
}).to.throw();
|
|
40
|
+
|
|
41
|
+
expect(function() {
|
|
42
|
+
const tse = new TurboStreamElement({ action: 'a', target: null });
|
|
43
|
+
}).to.throw();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
it(`Error is thrown when attribute "target" is malformed`, function() {
|
|
48
|
+
expect(function() {
|
|
49
|
+
const tse = new TurboStreamElement({ action: 'a', target: 8 });
|
|
50
|
+
}).to.throw();
|
|
51
|
+
|
|
52
|
+
expect(function() {
|
|
53
|
+
const tse = new TurboStreamElement({ action: 'a', target: '' });
|
|
54
|
+
}).to.throw();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
it(`Error is thrown when attribute "targets" is malformed`, function() {
|
|
59
|
+
expect(function() {
|
|
60
|
+
const tse = new TurboStreamElement({ action: 'a', targets: 8 });
|
|
61
|
+
}).to.throw();
|
|
62
|
+
|
|
63
|
+
expect(function() {
|
|
64
|
+
const tse = new TurboStreamElement({ action: 'a', targets: '' });
|
|
65
|
+
}).to.throw();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
it(`Error is thrown when attributes "target" and "targets" are present`, function() {
|
|
70
|
+
expect(function() {
|
|
71
|
+
const tse = new TurboStreamElement({ action: 'a', target: 't', targets: 'ts' });
|
|
72
|
+
}).to.throw();
|
|
73
|
+
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
});
|