Skip to content

Commit d5c41e8

Browse files
flotwigchrisbreiding
authored andcommitted
fix(proxy-logging): use constant consoleProps object (#18207)
Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>
1 parent 063128b commit d5c41e8

File tree

5 files changed

+133
-82
lines changed

5 files changed

+133
-82
lines changed

packages/driver/cypress/integration/commands/net_stubbing_spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ const testFail = (cb, expectedDocsUrl = 'https://on.cypress.io/intercept') => {
1111
})
1212
}
1313

14-
// TODO: Network retries leak between tests, causing flake.
15-
describe('network stubbing', { retries: 2 }, function () {
14+
describe('network stubbing', function () {
1615
const { $, _, sinon, state, Promise } = Cypress
1716

1817
beforeEach(function () {

packages/driver/cypress/integration/commands/xhr_spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2407,12 +2407,12 @@ describe('src/cy/commands/xhr', () => {
24072407
it('logs response', () => {
24082408
cy.then(function () {
24092409
cy.wrap(this).its('lastLog').invoke('invoke', 'consoleProps').should((consoleProps) => {
2410-
expect(consoleProps['Response Body']).to.deep.eq({
2410+
expect(consoleProps['Response Body'].trim()).to.deep.eq(JSON.stringify({
24112411
some: 'json',
24122412
foo: {
24132413
bar: 'baz',
24142414
},
2415-
})
2415+
}, null, 2))
24162416
})
24172417
})
24182418
})

packages/driver/cypress/integration/cypress/proxy-logging-spec.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { expect } from 'chai'
2+
13
describe('Proxy Logging', () => {
24
const { _ } = Cypress
35

@@ -43,12 +45,6 @@ describe('Proxy Logging', () => {
4345
}
4446
}
4547

46-
beforeEach(() => {
47-
// block race conditions caused by log update debouncing
48-
// @ts-ignore
49-
Cypress.config('logAttrsDelay', 0)
50-
})
51-
5248
context('request logging', () => {
5349
it('fetch log shows resource type, url, method, and status code and has expected snapshots and consoleProps', (done) => {
5450
fetch('/some-url')
@@ -106,6 +102,43 @@ describe('Proxy Logging', () => {
106102
})
107103
})
108104

105+
// @see https://github.com/cypress-io/cypress/issues/17656
106+
it('xhr log has response body/status code', (done) => {
107+
cy.window()
108+
.then((win) => {
109+
cy.on('log:changed', (log) => {
110+
try {
111+
expect(log.snapshots.map((v) => v.name)).to.deep.eq(['request', 'response'])
112+
expect(log.consoleProps['Response Headers']).to.include({
113+
'x-powered-by': 'Express',
114+
})
115+
116+
expect(log.consoleProps['Response Body']).to.include('Cannot GET /some-url')
117+
expect(log.consoleProps['Response Status Code']).to.eq(404)
118+
119+
expect(log.renderProps).to.include({
120+
indicator: 'bad',
121+
message: 'GET 404 /some-url',
122+
})
123+
124+
expect(Object.keys(log.consoleProps)).to.deep.eq(
125+
['Event', 'Resource Type', 'Method', 'URL', 'Request went to origin?', 'XHR', 'groups', 'Request Headers', 'Response Status Code', 'Response Headers', 'Response Body'],
126+
)
127+
128+
done()
129+
} catch (err) {
130+
// eslint-disable-next-line no-console
131+
console.log('assertion error, retrying', err)
132+
}
133+
})
134+
135+
const xhr = new win.XMLHttpRequest()
136+
137+
xhr.open('GET', '/some-url')
138+
xhr.send()
139+
})
140+
})
141+
109142
it('does not log an unintercepted non-xhr/fetch request', (done) => {
110143
const img = new Image()
111144
const logs: any[] = []

packages/driver/src/cypress/log.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const SNAPSHOT_PROPS = 'id snapshots $el url coords highlightAttr scrollBy viewp
1818
const DISPLAY_PROPS = 'id alias aliasType callCount displayName end err event functionName hookId instrument isStubbed group message method name numElements showError numResponses referencesAlias renderProps state testId timeout type url visible wallClockStartedAt testCurrentRetry'.split(' ')
1919
const BLACKLIST_PROPS = 'snapshots'.split(' ')
2020

21-
let delay = null
2221
let counter = 0
2322

2423
const { HIGHLIGHT_ATTR } = $Snapshots
@@ -113,10 +112,6 @@ const setCounter = (num) => {
113112
return counter = num
114113
}
115114

116-
const setDelay = (val) => {
117-
return delay = val != null ? val : 4
118-
}
119-
120115
const defaults = function (state, config, obj) {
121116
const instrument = obj.instrument != null ? obj.instrument : 'command'
122117

@@ -523,12 +518,6 @@ export default {
523518
counter = 0
524519
const logs = {}
525520

526-
// give us the ability to change the delay for firing
527-
// the change event, or default it to 4
528-
if (delay == null) {
529-
delay = setDelay(config('logAttrsDelay'))
530-
}
531-
532521
const trigger = function (log, event) {
533522
// bail if we never fired our initial log event
534523
if (!log._hasInitiallyLogged) {

packages/driver/src/cypress/proxy-logging.ts

Lines changed: 91 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -87,66 +87,7 @@ function getRequestLogConfig (req: Omit<ProxyRequest, 'log'>): Partial<Cypress.L
8787
url: req.preRequest.url,
8888
method: req.preRequest.method,
8989
timeout: 0,
90-
consoleProps: () => {
91-
// high-level request information
92-
const consoleProps = {
93-
'Resource Type': req.preRequest.resourceType,
94-
Method: req.preRequest.method,
95-
URL: req.preRequest.url,
96-
'Request went to origin?': req.flags.stubbed ? 'no (response was stubbed, see below)' : 'yes',
97-
}
98-
99-
if (req.flags.reqModified) consoleProps['Request modified?'] = 'yes'
100-
101-
if (req.flags.resModified) consoleProps['Response modified?'] = 'yes'
102-
103-
// details on matched XHR/intercept
104-
if (req.xhr) consoleProps['XHR'] = req.xhr.xhr
105-
106-
if (req.interceptions.length) {
107-
if (req.interceptions.length > 1) {
108-
consoleProps['Matched `cy.intercept()`s'] = req.interceptions.map(formatInterception)
109-
} else {
110-
consoleProps['Matched `cy.intercept()`'] = formatInterception(req.interceptions[0])
111-
}
112-
}
113-
114-
if (req.stack) {
115-
consoleProps['groups'] = () => {
116-
return [
117-
{
118-
name: 'Initiator',
119-
items: [req.stack],
120-
label: false,
121-
},
122-
]
123-
}
124-
}
125-
126-
// details on request/response/errors
127-
consoleProps['Request Headers'] = req.preRequest.headers
128-
129-
if (req.responseReceived) {
130-
_.assign(consoleProps, {
131-
'Response Status Code': req.responseReceived.status,
132-
'Response Headers': req.responseReceived.headers,
133-
})
134-
}
135-
136-
let resBody
137-
138-
if (req.xhr) {
139-
consoleProps['Response Body'] = req.xhr.responseBody
140-
} else if ((resBody = _.chain(req.interceptions).last().get('interception.response.body').value())) {
141-
consoleProps['Response Body'] = resBody
142-
}
143-
144-
if (req.error) {
145-
consoleProps['Error'] = req.error
146-
}
147-
148-
return consoleProps
149-
},
90+
consoleProps: () => req.consoleProps,
15091
renderProps: () => {
15192
function getIndicator (): 'aborted' | 'pending' | 'successful' | 'bad' {
15293
if (!req.responseReceived) {
@@ -211,9 +152,89 @@ class ProxyRequest {
211152
resModified?: boolean
212153
} = {}
213154

155+
// constant reference to consoleProps so changes reach the console
156+
// @see https://github.com/cypress-io/cypress/issues/17656
157+
readonly consoleProps: any
158+
214159
constructor (preRequest: BrowserPreRequest, opts?: Partial<ProxyRequest>) {
215160
this.preRequest = preRequest
216161
opts && _.assign(this, opts)
162+
163+
// high-level request information
164+
this.consoleProps = {
165+
'Resource Type': preRequest.resourceType,
166+
Method: preRequest.method,
167+
URL: preRequest.url,
168+
}
169+
170+
this.updateConsoleProps()
171+
}
172+
173+
updateConsoleProps () {
174+
const { consoleProps } = this
175+
176+
consoleProps['Request went to origin?'] = this.flags.stubbed ? 'no (response was stubbed, see below)' : 'yes'
177+
178+
if (this.flags.reqModified) consoleProps['Request modified?'] = 'yes'
179+
180+
if (this.flags.resModified) consoleProps['Response modified?'] = 'yes'
181+
182+
// details on matched XHR/intercept
183+
if (this.xhr) consoleProps['XHR'] = this.xhr.xhr
184+
185+
if (this.interceptions.length) {
186+
if (this.interceptions.length > 1) {
187+
consoleProps['Matched `cy.intercept()`s'] = this.interceptions.map(formatInterception)
188+
} else {
189+
consoleProps['Matched `cy.intercept()`'] = formatInterception(this.interceptions[0])
190+
}
191+
}
192+
193+
if (this.stack) {
194+
consoleProps['groups'] = () => {
195+
return [
196+
{
197+
name: 'Initiator',
198+
items: [this.stack],
199+
label: false,
200+
},
201+
]
202+
}
203+
}
204+
205+
// ensure these fields are always ordered correctly regardless of when they are added
206+
['Response Status Code', 'Response Headers', 'Response Body', 'Request Headers', 'Request Body'].forEach((k) => delete consoleProps[k])
207+
208+
// details on request
209+
consoleProps['Request Headers'] = this.preRequest.headers
210+
211+
const reqBody = _.chain(this.interceptions).last().get('interception.request.body').value()
212+
213+
if (reqBody) consoleProps['Request Body'] = reqBody
214+
215+
if (this.responseReceived) {
216+
_.assign(consoleProps, {
217+
'Response Status Code': this.responseReceived.status,
218+
'Response Headers': this.responseReceived.headers,
219+
})
220+
}
221+
222+
// details on response
223+
let resBody
224+
225+
if (this.xhr) {
226+
if (!consoleProps['Response Headers']) consoleProps['Response Headers'] = this.xhr.responseHeaders
227+
228+
if (!consoleProps['Response Status Code']) consoleProps['Response Status Code'] = this.xhr.xhr.status
229+
230+
consoleProps['Response Body'] = this.xhr.xhr.response
231+
} else if ((resBody = _.chain(this.interceptions).last().get('interception.response.body').value())) {
232+
consoleProps['Response Body'] = resBody
233+
}
234+
235+
if (this.error) {
236+
consoleProps['Error'] = this.error
237+
}
217238
}
218239

219240
setFlag = (flag: keyof ProxyRequest['flags']) => {
@@ -310,7 +331,15 @@ export default class ProxyLogging {
310331
}
311332

312333
proxyRequest.responseReceived = responseReceived
313-
proxyRequest.log?.snapshot('response').end()
334+
335+
proxyRequest.updateConsoleProps()
336+
337+
// @ts-ignore
338+
const hasResponseSnapshot = proxyRequest.log?.get('snapshots')?.find((v) => v.name === 'response')
339+
340+
if (!hasResponseSnapshot) proxyRequest.log?.snapshot('response')
341+
342+
proxyRequest.log?.end()
314343
}
315344

316345
private updateRequestWithError (error: RequestError): void {
@@ -321,6 +350,7 @@ export default class ProxyLogging {
321350
}
322351

323352
proxyRequest.error = $errUtils.makeErrFromObj(error.error)
353+
proxyRequest.updateConsoleProps()
324354
proxyRequest.log?.snapshot('error').error(proxyRequest.error)
325355
}
326356

0 commit comments

Comments
 (0)