Skip to content

Commit 3bfbf90

Browse files
committed
Update htk utils and use it to improve rule explanation text
1 parent 2c6aaf8 commit 3bfbf90

File tree

5 files changed

+53
-59
lines changed

5 files changed

+53
-59
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
"@graphql-tools/utils": "^8.8.0",
161161
"@httptoolkit/httpolyglot": "^3.0.0",
162162
"@httptoolkit/subscriptions-transport-ws": "^0.11.2",
163-
"@httptoolkit/util": "^0.1.6",
163+
"@httptoolkit/util": "^0.1.7",
164164
"@httptoolkit/websocket-stream": "^6.0.1",
165165
"@peculiar/asn1-schema": "^2.3.15",
166166
"@peculiar/asn1-x509": "^2.3.15",

src/rules/matchers.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as url from 'url';
44
import * as _ from 'lodash';
55
import { oneLine } from 'common-tags';
66
import * as multipart from 'parse-multipart-data';
7-
import { MaybePromise } from '@httptoolkit/util';
7+
import { MaybePromise, joinAnd } from '@httptoolkit/util';
88

99
import { CompletedRequest, Method, Explainable, OngoingRequest } from "../types";
1010
import {
@@ -659,14 +659,6 @@ export async function matchesAll(req: OngoingRequest, matchers: RequestMatcher[]
659659
}
660660

661661
export function explainMatchers(matchers: RequestMatcher[]) {
662-
if (matchers.length === 1) return matchers[0].explain();
663-
if (matchers.length === 2) {
664-
// With just two explanations, you can just combine them
665-
return `${matchers[0].explain()} ${matchers[1].explain()}`;
666-
}
667-
668-
// With 3+, we need to oxford comma separate explanations to make them readable
669-
return matchers.slice(0, -1)
670-
.map((m) => m.explain())
671-
.join(', ') + ', and ' + matchers.slice(-1)[0].explain();
662+
if (matchers.length === 2) return `${matchers[0].explain()} ${matchers[1].explain()}`;
663+
return joinAnd(matchers.map(m => m.explain()), { oxfordComma: true });
672664
}

src/rules/requests/request-rule.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as _ from 'lodash';
44

55
import { OngoingRequest, CompletedRequest, OngoingResponse, Explainable, RulePriority } from "../../types";
66
import { buildBodyReader, buildInitiatedRequest, waitForCompletedRequest } from '../../util/request-utils';
7-
import { MaybePromise } from '@httptoolkit/util';
7+
import { joinAnd, MaybePromise } from '@httptoolkit/util';
88

99
import * as matchers from "../matchers";
1010
import { type RequestStepDefinition } from "./request-step-definitions";
@@ -140,8 +140,13 @@ export class RequestRule implements RequestRule {
140140
}
141141

142142
explain(withoutExactCompletion = false): string {
143-
let explanation = `Match requests ${matchers.explainMatchers(this.matchers)}, ` +
144-
`and then ${explainSteps(this.steps)}`;
143+
let explanation = `Match ${
144+
this.priority === RulePriority.FALLBACK ? 'otherwise unmatched ' : ''
145+
}requests ${
146+
matchers.explainMatchers(this.matchers)
147+
}, and ${
148+
explainSteps(this.steps)
149+
}`;
145150

146151
if (this.completionChecker) {
147152
explanation += `, ${this.completionChecker.explain(
@@ -162,13 +167,8 @@ export class RequestRule implements RequestRule {
162167
}
163168

164169
export function explainSteps(steps: RequestStepDefinition[]) {
165-
if (steps.length === 1) return steps[0].explain();
166-
if (steps.length === 2) {
167-
return `${steps[0].explain()} then ${steps[1].explain()}`;
168-
}
169-
170-
// With 3+, we need to oxford comma separate explanations to make them readable
171-
return steps.slice(0, -1)
172-
.map((s) => s.explain())
173-
.join(', ') + ', and ' + steps.slice(-1)[0].explain();
170+
return joinAnd(steps.map(s => s.explain()), {
171+
finalSeparator: 'then ',
172+
oxfordComma: true
173+
});
174174
}

src/rules/websockets/websocket-rule.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
RulePriority
1010
} from "../../types";
1111
import { waitForCompletedRequest } from '../../util/request-utils';
12-
import { MaybePromise } from '@httptoolkit/util';
12+
import { joinAnd, MaybePromise } from '@httptoolkit/util';
1313

1414
import { validateMockRuleData } from '../rule-serialization';
1515

@@ -142,8 +142,13 @@ export class WebSocketRule implements WebSocketRule {
142142
}
143143

144144
explain(withoutExactCompletion = false): string {
145-
let explanation = `Match websockets ${matchers.explainMatchers(this.matchers)}, ` +
146-
`and then ${explainSteps(this.steps)}`;
145+
let explanation = `Match ${
146+
this.priority === RulePriority.FALLBACK ? 'otherwise unmatched ' : ''
147+
}websockets ${
148+
matchers.explainMatchers(this.matchers)
149+
}, and ${
150+
explainSteps(this.steps)
151+
}`;
147152

148153
if (this.completionChecker) {
149154
explanation += `, ${this.completionChecker.explain(
@@ -164,13 +169,8 @@ export class WebSocketRule implements WebSocketRule {
164169
}
165170

166171
export function explainSteps(steps: WebSocketStepDefinition[]) {
167-
if (steps.length === 1) return steps[0].explain();
168-
if (steps.length === 2) {
169-
return `${steps[0].explain()} then ${steps[1].explain()}`;
170-
}
171-
172-
// With 3+, we need to oxford comma separate explanations to make them readable
173-
return steps.slice(0, -1)
174-
.map((s) => s.explain())
175-
.join(', ') + ', and ' + steps.slice(-1)[0].explain();
172+
return joinAnd(steps.map(s => s.explain()), {
173+
finalSeparator: 'and ',
174+
oxfordComma: true
175+
});
176176
}

test/integration/explanations.spec.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ describe("Mockttp explanation messages", function () {
2323
let responseText = await response.text();
2424

2525
expect(responseText).to.include(`
26-
Match requests making GETs for /endpoint, and then respond with status 200 and body "1", once (seen 0).
27-
Match requests making GETs for /endpoint, and then respond with status 200 and body "2/3", twice (seen 0).
28-
Match requests making GETs for /endpoint, and then respond with status 200 and body "4/5/6", thrice (seen 0).
29-
Match requests making GETs for /endpoint, and then respond with status 200 and body "7/8/9/10", 4 times (seen 0).
30-
Match requests making GETs for /endpoint, and then respond with status 200 and body "forever", always (seen 0).
26+
Match requests making GETs for /endpoint, and respond with status 200 and body "1", once (seen 0).
27+
Match requests making GETs for /endpoint, and respond with status 200 and body "2/3", twice (seen 0).
28+
Match requests making GETs for /endpoint, and respond with status 200 and body "4/5/6", thrice (seen 0).
29+
Match requests making GETs for /endpoint, and respond with status 200 and body "7/8/9/10", 4 times (seen 0).
30+
Match requests making GETs for /endpoint, and respond with status 200 and body "forever", always (seen 0).
3131
`);
3232
});
3333

@@ -46,11 +46,11 @@ Match requests making GETs for /endpoint, and then respond with status 200 and b
4646
let responseText = await response.text();
4747

4848
expect(responseText).to.include(`
49-
Match requests making GETs for /endpoint, and then respond with status 200 and body "1", once (done).
50-
Match requests making GETs for /endpoint, and then respond with status 200 and body "2/3", twice (done).
51-
Match requests making GETs for /endpoint, and then respond with status 200 and body "4/5/6", thrice (done).
52-
Match requests making GETs for /endpoint, and then respond with status 200 and body "7/8/9/10", 4 times (seen 2).
53-
Match requests making GETs for /endpoint, and then respond with status 200 and body "forever", always (seen 0).
49+
Match requests making GETs for /endpoint, and respond with status 200 and body "1", once (done).
50+
Match requests making GETs for /endpoint, and respond with status 200 and body "2/3", twice (done).
51+
Match requests making GETs for /endpoint, and respond with status 200 and body "4/5/6", thrice (done).
52+
Match requests making GETs for /endpoint, and respond with status 200 and body "7/8/9/10", 4 times (seen 2).
53+
Match requests making GETs for /endpoint, and respond with status 200 and body "forever", always (seen 0).
5454
`);
5555
});
5656

@@ -62,6 +62,7 @@ Match requests making GETs for /endpoint, and then respond with status 200 and b
6262
await server.forPut("/endpointD").withQuery({ a: 1 }).always().thenCloseConnection();
6363
await server.forPut("/endpointE").forHost('abc.com').withExactQuery('?').thenTimeout();
6464
await server.forAnyWebSocket().thenForwardTo("google.com");
65+
await server.forUnmatchedRequest().withBody("b").delay(100).thenReply(200);
6566

6667
await fetch(server.urlFor("/endpointA/123"));
6768
let response = await fetch(server.urlFor("/non-existent-endpoint"));
@@ -70,13 +71,14 @@ Match requests making GETs for /endpoint, and then respond with status 200 and b
7071

7172
expect(text).to.include(`No rules were found matching this request.`);
7273
expect(text).to.include(`The configured rules are:
73-
Match requests for anything with headers including {"h":"v"}, and then respond with status 200 and a stream of response data.
74-
Match requests making GETs matching //endpointA/\\d+/, and then respond with status 200 and body "nice request!", once (done).
75-
Match requests making POSTs, for /endpointB, and with form data including {"key":"value"}, and then respond with status 500.
76-
Match requests making POSTs, for /endpointC, and with a JSON body equivalent to {"key":"value"}, and then respond with status 500.
77-
Match requests making PUTs, for /endpointD, and with a query including {"a":"1"}, and then close the connection, always (seen 0).
78-
Match requests making PUTs, for /endpointE, for host abc.com, and with a query exactly matching \`?\`, and then time out (never respond).
79-
Match websockets for anything, and then forward the websocket to google.com.
74+
Match otherwise unmatched requests for anything with body 'b', and wait 100ms, then respond with status 200.
75+
Match requests for anything with headers including {"h":"v"}, and respond with status 200 and a stream of response data.
76+
Match requests making GETs matching //endpointA/\\d+/, and respond with status 200 and body "nice request!", once (done).
77+
Match requests making POSTs, for /endpointB, and with form data including {"key":"value"}, and respond with status 500.
78+
Match requests making POSTs, for /endpointC, and with a JSON body equivalent to {"key":"value"}, and respond with status 500.
79+
Match requests making PUTs, for /endpointD, and with a query including {"a":"1"}, and close the connection, always (seen 0).
80+
Match requests making PUTs, for /endpointE, for host abc.com, and with a query exactly matching \`?\`, and time out (never respond).
81+
Match websockets for anything, and forward the websocket to google.com.
8082
`);
8183
});
8284

@@ -88,8 +90,8 @@ Match websockets for anything, and then forward the websocket to google.com.
8890
let text = await response.text();
8991

9092
expect(text).to.include(`The configured rules are:
91-
Match requests making POSTs for /endpointA, and then respond using provided callback.
92-
Match requests making POSTs for /endpointB, and then respond using provided callback (handleRequest).
93+
Match requests making POSTs for /endpointA, and respond using provided callback.
94+
Match requests making POSTs for /endpointB, and respond using provided callback (handleRequest).
9395
`);
9496
});
9597

@@ -180,16 +182,16 @@ as a proxy, instead of making requests to it directly`);
180182
const util = require('util');
181183
const explanation = util.inspect(endpoints);
182184
expect(explanation).to.include(
183-
'Match requests making GETs for /endpoint, and then respond with status 200 and body "first response", twice (seen 1).'
185+
'Match requests making GETs for /endpoint, and respond with status 200 and body "first response", twice (seen 1).'
184186
);
185187
expect(explanation).to.include(
186-
'Match requests making GETs for /endpoint, and then respond with status 200 and body "second response".'
188+
'Match requests making GETs for /endpoint, and respond with status 200 and body "second response".'
187189
);
188190
} else {
189191
const explanations = endpoints.map(p => (p as any).explanation);
190192
expect(explanations).to.deep.equal([
191-
'Match requests making GETs for /endpoint, and then respond with status 200 and body "first response", twice.',
192-
'Match requests making GETs for /endpoint, and then respond with status 200 and body "second response".'
193+
'Match requests making GETs for /endpoint, and respond with status 200 and body "first response", twice.',
194+
'Match requests making GETs for /endpoint, and respond with status 200 and body "second response".'
193195
]);
194196
}
195197
});

0 commit comments

Comments
 (0)