11import * as path from 'path' ;
2+ import * as net from 'net' ;
3+ import * as http from 'http' ;
24import { spawn , ChildProcess } from 'child_process' ;
35import * as StaticServer from 'static-server' ;
46import * as puppeteer from 'puppeteer' ;
57
8+ import { delay } from '../../src/util/promise' ;
9+
610import { expect } from '../test-setup' ;
711
812function 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 ( / P r o x y p o r t : ( \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+
3183describe ( '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