Skip to content

Commit e90a418

Browse files
committed
task: Report status text
1 parent a32f403 commit e90a418

File tree

6 files changed

+107
-2
lines changed

6 files changed

+107
-2
lines changed

android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ fun EditorScreen(
228228
setNetworkRequestListener(object : GutenbergView.NetworkRequestListener {
229229
override fun onNetworkRequest(request: org.wordpress.gutenberg.RecordedNetworkRequest) {
230230
android.util.Log.d("EditorActivity", "🌐 Network Request: ${request.method} ${request.url}")
231-
android.util.Log.d("EditorActivity", " Status: ${request.status}, Duration: ${request.duration}ms")
231+
android.util.Log.d("EditorActivity", " Status: ${request.status} ${request.statusText}, Duration: ${request.duration}ms")
232232

233233
// Log request headers
234234
if (request.requestHeaders.isNotEmpty()) {

ios/Demo-iOS/Sources/Views/EditorView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ private struct _EditorView: UIViewControllerRepresentable {
185185

186186
func editor(_ viewController: EditorViewController, didLogNetworkRequest request: RecordedNetworkRequest) {
187187
print("🌐 Network Request: \(request.method) \(request.url)")
188-
print(" Status: \(request.status), Duration: \(request.duration)ms")
188+
print(" Status: \(request.status) \(request.statusText), Duration: \(request.duration)ms")
189189

190190
// Log request headers
191191
if !request.requestHeaders.isEmpty {

ios/Sources/GutenbergKit/Sources/EditorViewControllerDelegate.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ public struct RecordedNetworkRequest {
176176
public let requestBody: String?
177177
/// The HTTP response status code
178178
public let status: Int
179+
/// The HTTP response status text (e.g., "OK", "Not Found")
180+
public let statusText: String
179181
/// The response headers
180182
public let responseHeaders: [String: String]
181183
/// The response body
@@ -199,6 +201,7 @@ public struct RecordedNetworkRequest {
199201
self.requestHeaders = requestHeaders
200202
self.requestBody = dict["requestBody"] as? String
201203
self.status = status
204+
self.statusText = dict["statusText"] as? String ?? ""
202205
self.responseHeaders = responseHeaders
203206
self.responseBody = dict["responseBody"] as? String
204207
self.duration = duration

src/utils/bridge.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export function onModalDialogClosed( dialogType ) {
181181
* @param {Object|null} requestData.requestHeaders The request headers object.
182182
* @param {string|null} requestData.requestBody The request body.
183183
* @param {number} requestData.status The HTTP response status code.
184+
* @param {string} requestData.statusText The HTTP response status text (e.g., "OK", "Not Found").
184185
* @param {Object|null} requestData.responseHeaders The response headers object.
185186
* @param {string|null} requestData.responseBody The response body.
186187
* @param {number} requestData.duration The request duration in milliseconds.

src/utils/fetch-interceptor.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ export function initializeFetchInterceptor() {
6464
// Capture response metadata immediately
6565
const responseClone = response.clone();
6666
responseStatus = response.status;
67+
const responseStatusText =
68+
response.statusText || getStatusText( response.status );
6769
responseHeaders = serializeHeaders( response.headers );
6870
const duration = Math.round( performance.now() - startTime );
6971

@@ -79,6 +81,7 @@ export function initializeFetchInterceptor() {
7981
),
8082
requestBody,
8183
status: responseStatus,
84+
statusText: responseStatusText,
8285
responseHeaders,
8386
responseBody: body,
8487
duration,
@@ -94,6 +97,7 @@ export function initializeFetchInterceptor() {
9497
),
9598
requestBody,
9699
status: responseStatus,
100+
statusText: responseStatusText,
97101
responseHeaders,
98102
responseBody: `[Error reading body: ${ error.message }]`,
99103
duration,
@@ -112,6 +116,7 @@ export function initializeFetchInterceptor() {
112116
requestHeaders: serializeHeaders( requestDetails.headers ),
113117
requestBody,
114118
status: 0,
119+
statusText: '',
115120
responseHeaders: {},
116121
responseBody: `[Network error: ${ error.message }]`,
117122
duration,
@@ -311,3 +316,58 @@ function serializeHeaders( headers ) {
311316

312317
return result;
313318
}
319+
320+
/**
321+
* Maps HTTP status codes to their standard status text.
322+
* Used as fallback when response.statusText is empty (common with HTTP/2).
323+
*
324+
* @param {number} status The HTTP status code.
325+
*
326+
* @return {string} The corresponding status text, or empty string if unknown.
327+
*/
328+
function getStatusText( status ) {
329+
const statusTexts = {
330+
// 1xx Informational
331+
100: 'Continue',
332+
101: 'Switching Protocols',
333+
// 2xx Success
334+
200: 'OK',
335+
201: 'Created',
336+
202: 'Accepted',
337+
203: 'Non-Authoritative Information',
338+
204: 'No Content',
339+
205: 'Reset Content',
340+
206: 'Partial Content',
341+
// 3xx Redirection
342+
300: 'Multiple Choices',
343+
301: 'Moved Permanently',
344+
302: 'Found',
345+
303: 'See Other',
346+
304: 'Not Modified',
347+
307: 'Temporary Redirect',
348+
308: 'Permanent Redirect',
349+
// 4xx Client Error
350+
400: 'Bad Request',
351+
401: 'Unauthorized',
352+
403: 'Forbidden',
353+
404: 'Not Found',
354+
405: 'Method Not Allowed',
355+
406: 'Not Acceptable',
356+
408: 'Request Timeout',
357+
409: 'Conflict',
358+
410: 'Gone',
359+
413: 'Payload Too Large',
360+
414: 'URI Too Long',
361+
415: 'Unsupported Media Type',
362+
422: 'Unprocessable Entity',
363+
429: 'Too Many Requests',
364+
// 5xx Server Error
365+
500: 'Internal Server Error',
366+
501: 'Not Implemented',
367+
502: 'Bad Gateway',
368+
503: 'Service Unavailable',
369+
504: 'Gateway Timeout',
370+
};
371+
372+
return statusTexts[ status ] || '';
373+
}

src/utils/fetch-interceptor.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ describe( 'initializeFetchInterceptor', () => {
3131
Promise.resolve( {
3232
ok: true,
3333
status: 200,
34+
statusText: 'OK',
3435
headers: new Headers( {
3536
'content-type': 'application/json',
3637
} ),
@@ -76,6 +77,45 @@ describe( 'initializeFetchInterceptor', () => {
7677
expect( window.fetch ).toBe( currentFetch );
7778
} );
7879

80+
it( 'should derive statusText from status code when empty (HTTP/2)', async () => {
81+
// Mock fetch with empty statusText (common with HTTP/2)
82+
global.fetch = vi.fn( () =>
83+
Promise.resolve( {
84+
ok: true,
85+
status: 201,
86+
statusText: '', // Empty, as with HTTP/2
87+
headers: new Headers( {
88+
'content-type': 'application/json',
89+
} ),
90+
clone() {
91+
return {
92+
headers: this.headers,
93+
text: () => Promise.resolve( '{}' ),
94+
blob: () =>
95+
Promise.resolve(
96+
new Blob( [ '{}' ], {
97+
type: 'application/json',
98+
} )
99+
),
100+
};
101+
},
102+
} )
103+
);
104+
105+
initializeFetchInterceptor();
106+
107+
await window.fetch( 'https://example.com/api', { method: 'POST' } );
108+
109+
await waitForAsyncLogging();
110+
111+
expect( bridge.onNetworkRequest ).toHaveBeenCalledWith(
112+
expect.objectContaining( {
113+
status: 201,
114+
statusText: 'Created', // Derived from status code
115+
} )
116+
);
117+
} );
118+
79119
describe( 'request header capture', () => {
80120
it( 'should capture headers from plain object with string URL', async () => {
81121
initializeFetchInterceptor();
@@ -98,6 +138,7 @@ describe( 'initializeFetchInterceptor', () => {
98138
Authorization: 'Bearer test-token',
99139
'X-Custom-Header': 'custom-value',
100140
} ),
141+
statusText: 'OK',
101142
} )
102143
);
103144
} );

0 commit comments

Comments
 (0)