Skip to content

Commit 0147fd8

Browse files
committedApr 22, 2025··
Add a smoke test for basic request interception & inspection
1 parent 4ab93ca commit 0147fd8

File tree

2 files changed

+113
-3
lines changed

2 files changed

+113
-3
lines changed
 

‎src/components/intercept/intercept-option.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ export class InterceptOption extends React.Component<InterceptOptionProps> {
241241
uiConfig={uiConfig}
242242
gridWidth={gridWidth}
243243

244+
data-interceptor-id={interceptor.id}
245+
244246
role={!this.expanded ? 'button' : 'section'}
245247
aria-expanded={isExpandable
246248
? this.expanded

‎test/integration/smoke-test.spec.ts

+111-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import * as path from 'path';
2+
import * as net from 'net';
3+
import * as http from 'http';
24
import { spawn, ChildProcess } from 'child_process';
35
import * as StaticServer from 'static-server';
46
import * as puppeteer from 'puppeteer';
57

8+
import { delay } from '../../src/util/promise';
9+
610
import { expect } from '../test-setup';
711

812
function startWebServer() {
@@ -28,13 +32,62 @@ async function startServer() {
2832
return serverProcess;
2933
}
3034

35+
async function getProxyPort(page: puppeteer.Page) {
36+
await page.waitForSelector('[data-interceptor-id=manual-setup]');
37+
38+
return await page.evaluate(() => {
39+
const proxyPortText = document.querySelector('[data-interceptor-id=manual-setup] span');
40+
41+
const proxyPortMatch = proxyPortText?.textContent?.match(/Proxy port: (\d+)/);
42+
if (!proxyPortMatch) throw new Error('Proxy port text not found');
43+
44+
return parseInt(proxyPortMatch[1], 10);
45+
});
46+
}
47+
48+
function sendRequest(proxyPort: number, url: string, options: http.RequestOptions = {}) {
49+
console.log(`Sending request to ${url} through proxy port ${proxyPort}`);
50+
return new Promise<void>((resolve, reject) => {
51+
const req = http.request(url, {
52+
...options,
53+
createConnection: () => {
54+
return net.connect({
55+
host: 'localhost',
56+
port: proxyPort
57+
});
58+
}
59+
});
60+
req.end();
61+
62+
req.on('error', reject);
63+
req.on('response', (res) => {
64+
console.log(`Response from ${url}: ${res.statusCode}`);
65+
res.resume();
66+
resolve();
67+
});
68+
});
69+
}
70+
71+
async function getRowContents(page: puppeteer.Page, rowIndex: number) {
72+
const cells = await page.evaluate((index) => {
73+
const row = document.querySelector(`[aria-rowindex="${index}"]`);
74+
if (!row) throw new Error(`Row ${index} not found`);
75+
76+
const cells = Array.from(row.querySelectorAll('[role=cell]'));
77+
return cells.map(cell => cell.textContent);
78+
}, rowIndex);
79+
80+
return cells.slice(1); // Skip the row marker cell
81+
}
82+
3183
describe('Smoke test', function () {
3284
this.timeout(10000);
3385

3486
let browser: puppeteer.Browser;
3587
let server: ChildProcess;
88+
let page: puppeteer.Page;
3689

37-
beforeEach(async () => {
90+
before(async () => {
3891
[ browser, server ] = await Promise.all([
3992
puppeteer.launch({
4093
headless: true,
@@ -51,19 +104,74 @@ describe('Smoke test', function () {
51104
});
52105

53106
afterEach(async () => {
107+
await page.close();
108+
});
109+
110+
after(async () => {
54111
await Promise.all([
55112
browser.close(),
56-
server.kill()
113+
server.kill(),
114+
Promise.race([
115+
new Promise((resolve) => server.on('exit', resolve)),
116+
delay(5000).then(() => server.kill('SIGKILL'))
117+
])
57118
]);
58119
});
59120

60121
it('can load the app', async () => {
61-
const page = await browser.newPage();
122+
page = await browser.newPage();
62123
await page.goto('http://localhost:7654');
63124

64125
await page.waitForSelector('h1');
65126
const heading = await page.$eval('h1', (h1) => h1.innerHTML);
66127

67128
expect(heading).to.equal('Intercept HTTP');
68129
});
130+
131+
it('can show directly sent requests', async () => {
132+
page = await browser.newPage();
133+
await page.goto('http://localhost:7654');
134+
135+
const proxyPort = await getProxyPort(page);
136+
137+
// Sent in order, to make assertion order consistent
138+
await sendRequest(proxyPort, 'http://testserver.host/echo');
139+
await sendRequest(proxyPort, 'http://example.com/404');
140+
await sendRequest(proxyPort, 'http://testserver.host/anything', { method: 'POST' });
141+
142+
await page.click('nav a[href="/view"]');
143+
144+
await page.waitForSelector('[aria-rowindex]');
145+
const rowCount = await page.evaluate(() => {
146+
return document.querySelectorAll('[aria-rowindex]').length;
147+
});
148+
expect(rowCount).to.equal(3);
149+
150+
expect(await getRowContents(page, 1)).to.deep.equal([
151+
'GET', '200', 'Unknown client', 'testserver.host', '/echo'
152+
]);
153+
154+
expect(await getRowContents(page, 2)).to.deep.equal([
155+
'GET', '404', 'Unknown client', 'example.com', '/404'
156+
]);
157+
158+
expect(await getRowContents(page, 3)).to.deep.equal([
159+
'POST', '200', 'Unknown client', 'testserver.host', '/anything'
160+
]);
161+
162+
await page.click('[aria-rowindex="3"]');
163+
164+
// Check the basic request & response details are shown
165+
const requestSection = await page.waitForSelector('[aria-label="Request section"]');
166+
expect(await requestSection.evaluate(s => s.textContent)).to.include('http://testserver.host/anything');
167+
168+
const responseSection = await page.waitForSelector('[aria-label="Response section"]');
169+
expect(await responseSection.evaluate(s => s.textContent)).to.include('Status: 200 OK');
170+
171+
// Test the body is rendered & formatted (auto-indented JSON) OK
172+
const responseBody = await page.waitForSelector('[aria-label="Response Body section"]');
173+
expect(await responseBody.evaluate(s =>
174+
s.textContent?.replace(/\u00a0/g, ' ') // Replace nbsp with normal spaces, just for simplicity
175+
)).to.include(' "Host": "testserver.host:80"');
176+
});
69177
});

0 commit comments

Comments
 (0)
Please sign in to comment.