Skip to content

Commit 2ee73da

Browse files
authoredJan 29, 2025··
test: rework tests to node-only (#350)
Long ago it seems we stopped testing in a browser, so this removes the associated switches in the test files and moves to a node-only suite. Meanwhile, this moves to using c8 for coverage and also reworks the tests to hit a local server rather than a real one (previously was hitting `chaijs.com` and another domain or two which changed underneath it over time).
1 parent 28ea01a commit 2ee73da

File tree

5 files changed

+625
-1678
lines changed

5 files changed

+625
-1678
lines changed
 

‎eslint.config.mjs

+7-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@ export default [
1111
expect: 'readonly',
1212
chai: 'readonly',
1313
global: 'writable',
14-
request: 'readonly'
14+
request: 'readonly',
15+
AbortController: 'readonly',
1516
}
1617
}
1718
},
1819
{
1920
...js.configs.recommended,
2021
files: ['**/*.js']
2122
},
22-
23+
{
24+
rules: {
25+
'mocha/no-mocha-arrows': 'off'
26+
}
27+
}
2328
];

‎package-lock.json

+439-1,418
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"build:ts": "cd types && tsc",
4242
"start": "npm-run-all --parallel watch server",
4343
"server": "http-server -o -c-1",
44-
"test": "nyc --reporter=lcovonly --reporter=text-summary mocha",
44+
"test": "c8 --reporter=lcovonly --reporter=text-summary mocha",
4545
"coverage": "if [ -z \"$COVERALLS_REPO_TOKEN\" ]; then cat coverage/lcov.info | coveralls; fi",
4646
"eslint": "eslint"
4747
},
@@ -52,25 +52,26 @@
5252
"querystring": "qs"
5353
},
5454
"dependencies": {
55+
"@types/superagent": "^8.1.7",
5556
"charset": "^1.0.1",
5657
"cookiejar": "^2.1.4",
5758
"is-ip": "^5.0.1",
5859
"methods": "^1.1.2",
5960
"qs": "^6.12.1",
60-
"@types/superagent": "^8.1.7",
6161
"superagent": "^9"
6262
},
6363
"devDependencies": {
6464
"@eslint/js": "^9.3.0",
6565
"@types/chai": "^4.3.16",
66+
"c8": "^10.1.3",
6667
"chai": "^5.1.0",
6768
"coveralls": "^3.1.1",
6869
"eslint": "^9.3.0",
6970
"eslint-plugin-mocha": "^10.4.3",
7071
"http-server": "^14.1.1",
7172
"mocha": "^10.4.0",
7273
"npm-run-all2": "^6.2.0",
73-
"nyc": "^15.1.0",
74+
"polka": "^1.0.0-next.28",
7475
"prettier": "^3.2.5",
7576
"typescript": "^5.4.5"
7677
},

‎test/bootstrap/index.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import * as originalChai from 'chai';
22
import * as http from 'http';
33
// this import is available from defining `imports` in package.json
4-
import {default as project, request } from 'chai-http';
4+
import {default as project} from 'chai-http';
55

66
global.http = http;
77

88
global.should = originalChai.should();
99
global.expect = originalChai.expect;
1010

1111
global['chai'] = originalChai.use(project);
12-
13-
global.request = request;

‎test/request.js

+174-252
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
11
import superagent from 'superagent';
2+
import {request} from 'chai-http';
3+
import polka from 'polka';
4+
5+
const SERVER_URL = 'http://localhost:8008';
6+
27
describe('request', function () {
3-
const isNode = typeof process === 'object';
4-
const isBrowser = typeof window === 'object';
8+
let server;
9+
let aborter;
10+
11+
beforeEach(() => {
12+
aborter = new AbortController();
13+
server = polka();
14+
server.listen({
15+
port: 8008,
16+
signal: aborter.signal
17+
});
18+
});
19+
20+
afterEach(() => {
21+
aborter.abort();
22+
});
523

624
describe('Browser and Node.js', function () {
725
it('is present on chai', function () {
@@ -10,36 +28,47 @@ describe('request', function () {
1028
});
1129

1230
it('request method returns instanceof superagent', function () {
13-
const req = request.execute('').get('/');
31+
server.get('/', (_req, res) => {
32+
res.statusCode = 200;
33+
});
34+
const req = request.execute(SERVER_URL).get('/');
1435
req.should.be.instanceof(request.Request.super_);
15-
if (isNode) {
16-
req.should.be.instanceof(superagent.Request);
17-
}
36+
req.should.be.instanceof(superagent.Request);
1837
});
1938

2039
it('can request a web page', function (done) {
40+
server.get('/foo', (_req, res) => {
41+
res.statusCode = 200;
42+
res.setHeader('content-type', 'text/html');
43+
res.end('<h1>bleep bloop</h1>');
44+
});
45+
2146
request
22-
.execute('https://chaijs.com')
23-
.get('/guide/')
47+
.execute(SERVER_URL)
48+
.get('/foo')
2449
.end(function (err, res) {
2550
res.should.have.status(200);
2651
res.should.be.html;
2752
res.should.not.be.text;
2853
res.should.not.be.json;
2954
res.text.should.be.a('string').with.length.above(0);
3055

31-
// Slightly different behavior in SuperAgent in Node/browsers
32-
isNode && res.body.should.deep.equal({});
33-
isBrowser && expect(res.body).to.be.null;
56+
res.body.should.deep.equal({});
3457

3558
done(err);
3659
});
3760
});
3861

3962
it('can request JSON data', function (done) {
63+
server.get('/foo', (_req, res) => {
64+
res.statusCode = 200;
65+
res.setHeader('content-type', 'application/json');
66+
res.end('{"foo":"bar"}');
67+
});
68+
4069
request
41-
.execute('https://chaijs.com')
42-
.get('/package-lock.json')
70+
.execute(SERVER_URL)
71+
.get('/foo')
4372
.end(function (err, res) {
4473
res.should.have.status(200);
4574
res.should.be.json;
@@ -52,284 +81,177 @@ describe('request', function () {
5281
});
5382

5483
it('can read response headers', function (done) {
55-
this.timeout(5000);
84+
server.get('/foo', (_req, res) => {
85+
res.statusCode = 200;
86+
res.setHeader('content-type', 'application/json');
87+
res.setHeader('x-foo', '303');
88+
res.setHeader('x-bar', '808');
89+
res.end('{"foo":"bar"}');
90+
});
91+
5692
request
57-
.execute('https://webhook.site')
58-
.post('/token')
93+
.execute(SERVER_URL)
94+
.get('/foo')
5995
.end(function (err, res) {
60-
const uuid = res.body.uuid;
61-
request
62-
.execute('https://webhook.site')
63-
.get('/' + uuid)
64-
.query({'content-type': 'application/json'})
65-
.query({pragma: 'test1'})
66-
.query({location: 'test2'})
67-
.query({'x-api-key': 'test3'})
68-
.end(function (err, res) {
69-
res.should.have.status(200);
70-
request
71-
.execute('https://webhook.site')
72-
.get('/token/' + uuid + '/requests?sorting=newest&per_page=1')
73-
.end(function (err, res) {
74-
// Content-Type and Pragma are supported on Node and browser
75-
res.should.be.json;
76-
res.should.have.nested.property(
77-
'.body.data.0.query.content-type',
78-
'application/json'
79-
);
80-
res.should.have.nested.property(
81-
'.body.data.0.query.pragma',
82-
'test1'
83-
);
84-
85-
// When running in a browser, only "simple" headers are readable
86-
// https://www.w3.org/TR/cors/#simple-response-header
87-
isNode &&
88-
res.should.have.nested.property(
89-
'.body.data.0.query.location',
90-
'test2'
91-
);
92-
isNode &&
93-
res.should.have.nested.property(
94-
'.body.data.0.query.x-api-key',
95-
'test3'
96-
);
97-
isBrowser &&
98-
res.should.not.have.nested.property(
99-
'.body.data.0.query.location'
100-
);
101-
isBrowser &&
102-
res.should.not.have.nested.property(
103-
'.body.data.0.query.x-api-key'
104-
);
105-
106-
done(err);
107-
});
108-
});
96+
res.should.be.json;
97+
res.headers.should.have.property('x-foo', '303');
98+
res.headers.should.have.property('x-bar', '808');
99+
done(err);
109100
});
110101
});
111102

112103
it('succeeds when response has an error status', function (done) {
104+
server.get('/foo', (_req, res) => {
105+
res.statusCode = 404;
106+
res.end();
107+
});
113108
request
114-
.execute('https://chaijs.com')
115-
.get('/404')
109+
.execute(SERVER_URL)
110+
.get('/foo')
116111
.end(function (err, res) {
117112
res.should.have.status(404);
118113
done(err);
119114
});
120115
});
121116

122-
it('can be augmented with promises', function (done) {
123-
this.timeout(5000);
124-
let uuid = '';
125-
request
126-
.execute('https://webhook.site')
127-
.post('/token')
128-
.then(function (res) {
129-
uuid = res.body.uuid;
130-
return res.body.uuid;
131-
})
132-
.then(function (uuid) {
133-
return request
134-
.execute('https://webhook.site')
135-
.get('/' + uuid)
136-
.query({'content-type': 'application/json'})
137-
.query({'x-api-key': 'test3'});
138-
})
139-
.then(function (res) {
140-
res.should.have.status(200);
141-
return request
142-
.execute('https://webhook.site')
143-
.get('/token/' + uuid + '/requests?sorting=newest&per_page=1');
144-
})
145-
.then(function (res) {
146-
res.should.have.status(200);
147-
res.should.be.json;
148-
res.should.have.nested.property(
149-
'.body.data.0.query.content-type',
150-
'application/json'
151-
);
152-
res.should.have.nested.property(
153-
'.body.data.0.query.x-api-key',
154-
'test3'
155-
);
156-
})
157-
.then(function () {
158-
throw new Error('Testing catch');
159-
})
160-
.then(function () {
161-
throw new Error('This should not have fired');
162-
})
163-
.catch(function (err) {
164-
if (err.message !== 'Testing catch') {
165-
throw err;
166-
}
167-
})
168-
.then(done, done);
117+
it('can be augmented with promises', async function () {
118+
server.get('/foo', (_req, res) => {
119+
res.statusCode = 200;
120+
res.setHeader('content-type', 'application/json');
121+
res.end('{"foo":"bar"}');
122+
});
123+
const response = await request
124+
.execute(SERVER_URL)
125+
.get('/foo');
126+
response.should.have.status(200);
127+
response.should.be.json;
169128
});
170129

171-
it('can resolve a promise given status code of 404', function () {
172-
return request
173-
.execute('https://chaijs.com')
174-
.get('/404')
175-
.then(function (res) {
176-
res.should.have.status(404);
177-
});
130+
it('can resolve a promise given status code of 404', async function () {
131+
server.get('/foo', (_req, res) => {
132+
res.statusCode = 404;
133+
res.end();
134+
});
135+
136+
const response = await request
137+
.execute(SERVER_URL)
138+
.get('/foo');
139+
response.should.have.status(404);
178140
});
179141
});
180142

181-
isNode &&
182-
describe('Node.js', function () {
183-
it('can request a functioned "app"', function (done) {
184-
const app = function (req, res) {
185-
req.headers['x-api-key'].should.equal('testing');
186-
res.writeHeader(200, {'content-type': 'text/plain'});
187-
res.end('hello universe');
188-
};
143+
describe('Node.js', function () {
144+
it('can request a functioned "app"', function (done) {
145+
const app = function (req, res) {
146+
req.headers['x-api-key'].should.equal('testing');
147+
res.writeHeader(200, {'content-type': 'text/plain'});
148+
res.end('hello universe');
149+
};
189150

190-
request
191-
.execute(app)
192-
.get('/')
193-
.set('X-API-Key', 'testing')
194-
.end(function (err, res) {
195-
if (err) return done(err);
196-
res.should.have.status(200);
197-
res.text.should.equal('hello universe');
198-
done();
199-
});
200-
});
201-
202-
it('can request an already existing url', function (done) {
203-
const server = http.createServer(function (req, res) {
204-
req.headers['x-api-key'].should.equal('test2');
205-
res.writeHeader(200, {'content-type': 'text/plain'});
206-
res.end('hello world');
207-
});
208-
209-
server.listen(0, function () {
210-
request
211-
.execute('http://127.0.0.1:' + server.address().port)
212-
.get('/')
213-
.set('X-API-Key', 'test2')
214-
.end(function (err, res) {
215-
res.should.have.status(200);
216-
res.text.should.equal('hello world');
217-
server.once('close', function () {
218-
done(err);
219-
});
220-
server.close();
221-
});
151+
request
152+
.execute(app)
153+
.get('/')
154+
.set('X-API-Key', 'testing')
155+
.end(function (err, res) {
156+
if (err) return done(err);
157+
res.should.have.status(200);
158+
res.text.should.equal('hello universe');
159+
done();
222160
});
223-
});
224-
225-
it('agent can be used to persist cookies', function (done) {
226-
const app = function (req, res) {
227-
res.setHeader('Set-Cookie', 'mycookie=test');
228-
res.writeHeader(200, {'content-type': 'text/plain'});
229-
res.end('your cookie: ' + req.headers.cookie);
230-
};
231-
const agent = request.agent(app);
161+
});
232162

233-
agent
234-
.get('/')
235-
.then(function (res) {
236-
res.headers['set-cookie'][0].should.equal('mycookie=test');
237-
res.text.should.equal('your cookie: undefined');
238-
})
239-
.then(function () {
240-
return agent.get('/');
241-
})
242-
.then(function (res) {
243-
res.text.should.equal('your cookie: mycookie=test');
244-
agent.close();
245-
})
246-
.then(done, done);
163+
it('can request an already existing url', function (done) {
164+
const server = http.createServer(function (req, res) {
165+
req.headers['x-api-key'].should.equal('test2');
166+
res.writeHeader(200, {'content-type': 'text/plain'});
167+
res.end('hello world');
247168
});
248169

249-
it('automatically closes the server down once done with it', function (done) {
250-
const server = http.createServer(function (req, res) {
251-
res.writeHeader(200, {'content-type': 'text/plain'});
252-
res.end('hello world');
253-
});
254-
170+
server.listen(0, function () {
255171
request
256-
.execute(server)
172+
.execute('http://127.0.0.1:' + server.address().port)
257173
.get('/')
174+
.set('X-API-Key', 'test2')
258175
.end(function (err, res) {
259176
res.should.have.status(200);
260177
res.text.should.equal('hello world');
261-
should.not.exist(server.address());
262-
done(err);
178+
server.once('close', function () {
179+
done(err);
180+
});
181+
server.close();
263182
});
264183
});
184+
});
265185

266-
it('can use keepOpen() to not close the server', function (done) {
267-
const server = http.createServer(function (req, res) {
268-
res.writeHeader(200, {'content-type': 'text/plain'});
269-
res.end('hello world');
270-
});
271-
const cachedRequest = request.execute(server).keepOpen();
272-
server.listen = function () {
273-
throw new Error('listen was called when it shouldnt have been');
274-
};
275-
cachedRequest.get('/').end(function (err) {
276-
cachedRequest.get('/').end(function (err2) {
277-
server.close(function () {
278-
done(err || err2);
279-
});
280-
});
281-
});
186+
it('agent can be used to persist cookies', function (done) {
187+
const app = function (req, res) {
188+
res.setHeader('Set-Cookie', 'mycookie=test');
189+
res.writeHeader(200, {'content-type': 'text/plain'});
190+
res.end('your cookie: ' + req.headers.cookie);
191+
};
192+
const agent = request.agent(app);
193+
194+
agent
195+
.get('/')
196+
.then(function (res) {
197+
res.headers['set-cookie'][0].should.equal('mycookie=test');
198+
res.text.should.equal('your cookie: undefined');
199+
})
200+
.then(function () {
201+
return agent.get('/');
202+
})
203+
.then(function (res) {
204+
res.text.should.equal('your cookie: mycookie=test');
205+
agent.close();
206+
})
207+
.then(done, done);
208+
});
209+
210+
it('automatically closes the server down once done with it', function (done) {
211+
const server = http.createServer(function (_req, res) {
212+
res.writeHeader(200, {'content-type': 'text/plain'});
213+
res.end('hello world');
282214
});
283215

284-
it('can close server after using keepOpen()', function (done) {
285-
const server = http.createServer(function (req, res) {
286-
res.writeHeader(200, {'content-type': 'text/plain'});
287-
res.end('hello world');
288-
});
289-
const cachedRequest = request.execute(server).keepOpen();
290-
cachedRequest.close(function () {
216+
request
217+
.execute(server)
218+
.get('/')
219+
.end(function (err, res) {
220+
res.should.have.status(200);
221+
res.text.should.equal('hello world');
291222
should.not.exist(server.address());
292-
done();
223+
done(err);
293224
});
294-
});
295225
});
296226

297-
isBrowser &&
298-
describe('Browser', function () {
299-
it('cannot request a functioned "app"', function () {
300-
function tryToRequestAFunctionedApp() {
301-
const app = function () {};
302-
request.execute(app);
303-
}
304-
expect(tryToRequestAFunctionedApp).to.throw(
305-
Error,
306-
/http.createServer is not a function|createServer/
307-
);
227+
it('can use keepOpen() to not close the server', function (done) {
228+
const server = http.createServer(function (_req, res) {
229+
res.writeHeader(200, {'content-type': 'text/plain'});
230+
res.end('hello world');
308231
});
232+
const cachedRequest = request.execute(server).keepOpen();
233+
server.listen = function () {
234+
throw new Error('listen was called when it shouldnt have been');
235+
};
236+
cachedRequest.get('/').end(function (err) {
237+
cachedRequest.get('/').end(function (err2) {
238+
server.close(function () {
239+
done(err || err2);
240+
});
241+
});
242+
});
243+
});
309244

310-
it('agent can be used to persist cookies', function (done) {
311-
const agent = request.agent('https://httpbin.org');
312-
313-
agent
314-
.get('/cookies/set')
315-
.query({foo: 'bar', biz: 'baz'})
316-
.then(function (res) {
317-
// When running in a web browser, cookies are protected and cannot be read by SuperAgent.
318-
// They ARE set, but only the browser has access to them.
319-
expect(res.headers['set-cookie']).to.be.undefined;
320-
res.should.not.have.cookie('foo');
321-
res.should.not.have.cookie('bar');
322-
})
323-
.then(function () {
324-
// When making a subsequent request to the same server, the cookies will be sent
325-
return agent.get('/cookies');
326-
})
327-
.then(function (res) {
328-
// HttpBin echoes the cookies back as JSON
329-
res.body.cookies.foo.should.equal('bar');
330-
res.body.cookies.biz.should.equal('baz');
331-
})
332-
.then(done, done);
245+
it('can close server after using keepOpen()', function (done) {
246+
const server = http.createServer(function (_req, res) {
247+
res.writeHeader(200, {'content-type': 'text/plain'});
248+
res.end('hello world');
249+
});
250+
const cachedRequest = request.execute(server).keepOpen();
251+
cachedRequest.close(function () {
252+
should.not.exist(server.address());
253+
done();
333254
});
334255
});
256+
});
335257
});

0 commit comments

Comments
 (0)
Please sign in to comment.