• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jwalton / node-supertest-fetch / 9698097240

27 Jun 2024 02:06PM UTC coverage: 85.67% (+6.0%) from 79.656%
9698097240

push

github

jwalton
feat: All external dependencies removed in #173.

BREAKING CHANGE: Now requires node 18 or higher.

77 of 98 branches covered (78.57%)

Branch coverage included in aggregate %.

479 of 551 relevant lines covered (86.93%)

54.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/src/Test.ts
1
import assert from 'node:assert';
4✔
2
import Server from './Server';
4✔
3
import {
4✔
4
    Assertion,
4✔
5
    StatusAssertion,
4✔
6
    BodyAssertion,
4✔
7
    HeaderAssertion,
4✔
8
    AssertionContext,
4✔
9
    HeaderValue,
4✔
10
} from './Assertions';
4✔
11

4✔
12
export default class Test implements PromiseLike<Response> {
104✔
13
    private _pServer: Promise<Server>;
104✔
14
    private _description: string;
104✔
15
    private _result: Promise<Response>;
104✔
16
    private _assertions: Assertion[] = [];
104✔
17

104✔
18
    constructor(pServer: Promise<Server>, url: string | Request, init?: RequestInit) {
104✔
19
        this._pServer = pServer;
104✔
20
        if (typeof url === 'string') {
104✔
21
            const method = ((init && init.method) || 'get').toUpperCase();
104✔
22
            this._description = `${method} ${url}`;
104✔
23
        } else {
104!
24
            const method = (url.method || (init && init.method) || 'get').toUpperCase();
×
25
            this._description = `${method} ${url}`;
×
26
        }
×
27

104✔
28
        this._result = this._fetchPriv(url, init);
104✔
29
    }
104✔
30

104✔
31
    private _fetchPriv(url: string | Request, init?: RequestInit) {
104✔
32
        return this._pServer.then((server) => {
104✔
33
            let result: Promise<Response>;
104✔
34

104✔
35
            if (typeof url === 'string') {
104✔
36
                const requestUrl = server.url + url;
104✔
37
                result = fetch(requestUrl, init);
104✔
38
            } else {
104!
39
                const request = new Request(server.url + url.url, {
×
40
                    method: url.method,
×
41
                    headers: url.headers,
×
42
                    body: url.body,
×
43
                    redirect: url.redirect,
×
44
                });
×
45
                result = fetch(request, init);
×
46
            }
×
47
            return result;
104✔
48
        });
104✔
49
    }
104✔
50

104✔
51
    /**
104✔
52
     * Verify status code and body.
104✔
53
     *
104✔
54
     * @param statusCode - The expected status code.
104✔
55
     * @param [body] - The expected body.
104✔
56
     */
104✔
57
    expect(statusCode: number, body?: unknown): this;
104✔
58

104✔
59
    /**
104✔
60
     * Verify a header exists.  This is an alias for `expectHeader()`.
104✔
61
     *
104✔
62
     * @param header - The header name.
104✔
63
     * @param value - The expected header value.
104✔
64
     */
104✔
65
    expect(header: string, value: HeaderValue): this;
104✔
66

104✔
67
    /**
104✔
68
     * Verify body exists.  This is an alias for `expectBody()`.
104✔
69
     *
104✔
70
     * @param body - The expected body.  If this is an object, then we expect
104✔
71
     *
104✔
72
     * @param value - The expected header value.
104✔
73
     */
104✔
74
    expect(body: unknown): this;
104✔
75

104✔
76
    expect(a: unknown, b?: unknown): this {
104✔
77
        if (typeof a === 'number') {
44✔
78
            this.expectStatus(a);
16✔
79
            if (arguments.length === 2) {
16✔
80
                this.expectBody(b);
4✔
81
            }
4✔
82
        } else if (typeof a === 'string' && arguments.length === 2) {
44✔
83
            if (!isHeaderValue(b)) {
16!
84
                throw new Error(`Invalid header value: ${b}`);
×
85
            }
×
86
            this.expectHeader(a, b);
16✔
87
        } else {
28✔
88
            this.expectBody(a);
12✔
89
        }
12✔
90
        return this;
44✔
91
    }
44✔
92

104✔
93
    /**
104✔
94
     * Verify the status code (and optionally the status text) of a response.
104✔
95
     *
104✔
96
     * @param statusCode - Expected status code.
104✔
97
     * @param [statusText] - Expected status text.
104✔
98
     */
104✔
99
    expectStatus(statusCode: number, statusText?: string) {
104✔
100
        this._assertions.push(new StatusAssertion(statusCode, statusText));
92✔
101
        return this;
92✔
102
    }
92✔
103

104✔
104
    /**
104✔
105
     * Verify the body of a response.
104✔
106
     *
104✔
107
     * @param expectedBody - The body to verify.  This can be either a string, a regex,
104✔
108
     *   or a JSON object.  If an object, this will treat the response like JSON
104✔
109
     *   data.  Passing `null` or `undefined` to expectBody will verify that the
104✔
110
     *   response has no content-length or transfer-encoding header.
104✔
111
     */
104✔
112
    expectBody(expectedBody: unknown) {
104✔
113
        this._assertions.push(new BodyAssertion(expectedBody));
80✔
114
        return this;
80✔
115
    }
80✔
116

104✔
117
    /**
104✔
118
     * Verifies a header exists and has the specified value.
104✔
119
     *
104✔
120
     * @param name - The header to check for.
104✔
121
     * @param value - The value to verify.  If `null` or `undefined`, this will
104✔
122
     *   verify the header is not present.
104✔
123
     */
104✔
124
    expectHeader(name: string, value: HeaderValue) {
104✔
125
        this._assertions.push(new HeaderAssertion(name, value));
68✔
126
        return this;
68✔
127
    }
68✔
128

104✔
129
    end() {
104✔
130
        const expected: object = {};
104✔
131
        const actual: object = {};
104✔
132
        const context: AssertionContext = {};
104✔
133

104✔
134
        return this._result.then(
104✔
135
            async (response) => {
104✔
136
                const server = await this._pServer;
104✔
137
                server.close();
104✔
138

104✔
139
                let message: string | undefined;
104✔
140
                for (const assertion of this._assertions) {
104✔
141
                    const assertionMessage = await assertion.execute(
240✔
142
                        actual,
240✔
143
                        expected,
240✔
144
                        response,
240✔
145
                        context
240✔
146
                    );
240✔
147
                    message = message || assertionMessage;
240✔
148
                }
240✔
149
                assert.deepStrictEqual(
104✔
150
                    actual,
104✔
151
                    expected,
104✔
152
                    message ? this._should(message) : undefined
104✔
153
                );
104✔
154

104✔
155
                return response;
104✔
156
            },
104✔
157
            (err) =>
104✔
158
                this._pServer.then((server) => {
×
159
                    server.close();
×
160
                    throw err;
×
161
                })
×
162
        );
104✔
163
    }
104✔
164

104✔
165
    /**
104✔
166
     * Tests are 'thennable', so you can treat them like a Promise and get back
104✔
167
     * the WHAT-WG fetch response.
104✔
168
     */
104✔
169
    then<TResult1 = Response, TResult2 = never>(
104✔
170
        onfulfilled?: ((value: Response) => TResult1 | PromiseLike<TResult1>) | undefined | null,
84✔
171
        onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | undefined | null
84✔
172
    ): PromiseLike<TResult1 | TResult2> {
84✔
173
        return this.end().then(onfulfilled, onrejected);
84✔
174
    }
84✔
175

104✔
176
    private _should(message: string) {
104✔
177
        return `Request "${this._description}" should ${message}`;
32✔
178
    }
32✔
179

104✔
180
    /**
104✔
181
     * Returns the JSON contents of the response.
104✔
182
     */
104✔
183
    async json() {
104✔
184
        const response = await this.end();
8✔
185
        return await response.json();
4✔
186
    }
4✔
187
}
104✔
188

4✔
189
function isHeaderValue(v: unknown): v is HeaderValue {
16✔
190
    return (
16✔
191
        v === null ||
16✔
192
        typeof v === 'string' ||
16✔
193
        (Array.isArray(v) && v.every((s) => typeof s === 'string')) ||
16!
194
        typeof v === 'number' ||
16✔
195
        v instanceof RegExp
4✔
196
    );
16✔
197
}
16✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc