1
1
import * as path from 'path' ;
2
+ import * as net from 'net' ;
3
+ import * as http from 'http' ;
2
4
import { spawn , ChildProcess } from 'child_process' ;
3
5
import * as StaticServer from 'static-server' ;
4
6
import * as puppeteer from 'puppeteer' ;
5
7
8
+ import { delay } from '../../src/util/promise' ;
9
+
6
10
import { expect } from '../test-setup' ;
7
11
8
12
function startWebServer ( ) {
@@ -28,13 +32,62 @@ async function startServer() {
28
32
return serverProcess ;
29
33
}
30
34
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
+
31
83
describe ( 'Smoke test' , function ( ) {
32
84
this . timeout ( 10000 ) ;
33
85
34
86
let browser : puppeteer . Browser ;
35
87
let server : ChildProcess ;
88
+ let page : puppeteer . Page ;
36
89
37
- beforeEach ( async ( ) => {
90
+ before ( async ( ) => {
38
91
[ browser , server ] = await Promise . all ( [
39
92
puppeteer . launch ( {
40
93
headless : true ,
@@ -51,19 +104,74 @@ describe('Smoke test', function () {
51
104
} ) ;
52
105
53
106
afterEach ( async ( ) => {
107
+ await page . close ( ) ;
108
+ } ) ;
109
+
110
+ after ( async ( ) => {
54
111
await Promise . all ( [
55
112
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
+ ] )
57
118
] ) ;
58
119
} ) ;
59
120
60
121
it ( 'can load the app' , async ( ) => {
61
- const page = await browser . newPage ( ) ;
122
+ page = await browser . newPage ( ) ;
62
123
await page . goto ( 'http://localhost:7654' ) ;
63
124
64
125
await page . waitForSelector ( 'h1' ) ;
65
126
const heading = await page . $eval ( 'h1' , ( h1 ) => h1 . innerHTML ) ;
66
127
67
128
expect ( heading ) . to . equal ( 'Intercept HTTP' ) ;
68
129
} ) ;
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
+ } ) ;
69
177
} ) ;
0 commit comments