\n' +
129 | `
Cy-api: ${name}
\n` +
130 | '
\n' +
131 | '
Request:\n' +
132 | '
' +
133 | formatRequest(options) +
134 | '\n
'
135 | }
136 |
137 | cy.request({
138 | ...options,
139 | log: false,
140 | })
141 | .then(
142 | ({ duration, body, status, headers, requestHeaders, statusText }) => {
143 | return printResponse(
144 | container,
145 | hasApiMessages,
146 | messagesEndpoint,
147 | normalizedTypes,
148 | normalizedNamespaces,
149 | apiOptions.displayRequest,
150 | ).then(({ messages }) => {
151 | return cy.wrap(
152 | {
153 | messages,
154 | duration,
155 | body,
156 | status,
157 | headers,
158 | requestHeaders,
159 | statusText,
160 | },
161 | { log: false },
162 | )
163 | })
164 | },
165 | )
166 | .then(
167 | ({
168 | messages,
169 | duration,
170 | body,
171 | status,
172 | headers,
173 | requestHeaders,
174 | statusText,
175 | }) => {
176 | // render the response object
177 | // TODO render headers?
178 | if (apiOptions.displayRequest) {
179 | container.innerHTML +=
180 | '
\n' +
181 | `
Response: ${status} ${duration}ms\n` +
182 | '
' +
183 | formatResponse(body, headers) +
184 | '\n
'
185 | }
186 |
187 | // log the response
188 | Cypress.log({
189 | name: 'response',
190 | message: options.url,
191 | consoleProps() {
192 | return {
193 | type: typeof body,
194 | response: body,
195 | }
196 | },
197 | })
198 |
199 | for (const type of normalizedTypes) {
200 | addOnClickFilter(type)
201 | }
202 |
203 | for (const namespace of normalizedNamespaces) {
204 | addOnClickFilter(namespace)
205 | }
206 |
207 | win.scrollTo(0, doc.body.scrollHeight)
208 |
209 | return {
210 | messages,
211 | // original response information
212 | duration,
213 | body,
214 | status,
215 | statusText,
216 | headers,
217 | requestHeaders,
218 | }
219 | },
220 | )
221 | },
222 | )
223 |
224 | const printResponse = (
225 | container: HTMLElement,
226 | hasApiMessages: boolean,
227 | messagesEndpoint: string,
228 | normalizedTypes: string[],
229 | normalizedNamespaces: string[],
230 | displayRequest = true,
231 | ) => {
232 | let messages: Message[] = []
233 | if (hasApiMessages) {
234 | return cy
235 | .request({
236 | url: messagesEndpoint,
237 | log: false,
238 | failOnStatusCode: false, // maybe there is no endpoint with logs
239 | })
240 | .then((res) => {
241 | messages = get(res, 'body.messages', [])
242 | if (messages.length) {
243 | const types = uniq(map(messages, 'type')).sort()
244 | // types will be like
245 | // ['console', 'debug', 'util.debuglog']
246 | const namespaces = types.map((type) => {
247 | return {
248 | type,
249 | namespaces: uniq(
250 | map(filter(messages, { type }), 'namespace'),
251 | ).sort(),
252 | }
253 | })
254 | // namespaces will be like
255 | // [
256 | // {type: 'console', namespaces: ['log']},
257 | // {type: 'util.debuglog', namespaces: ['HTTP']}
258 | // ]
259 | if (displayRequest) {
260 | container.innerHTML +=
261 | '\n' +
263 | `
Server logs`
264 |
265 | if (types.length) {
266 | for (const type of types) {
267 | const normalizedType = normalize(type)
268 | normalizedTypes.push(normalizedType)
269 | container.innerHTML += `\n
${type}`
270 | }
271 | container.innerHTML += '
\n'
272 | }
273 | if (namespaces.length) {
274 | container.innerHTML +=
275 | '\n' +
276 | namespaces
277 | .map((n) => {
278 | if (!n.namespaces.length) {
279 | return ''
280 | }
281 | return n.namespaces
282 | .map((namespace) => {
283 | const normalizedNamespace = normalize(n.type, namespace)
284 | normalizedNamespaces.push(normalizedNamespace)
285 | return `\n
${n.type}.${namespace}`
288 | })
289 | .join('')
290 | })
291 | .join('') +
292 | '
\n'
293 | }
294 |
295 | container.innerHTML +=
296 | '\n
' +
297 | messages
298 | .map((m) => {
299 | const s =
300 | typeof m.message === 'string'
301 | ? m.message
302 | : JSON.stringify(m.message)
303 | const html = `${m.type} ${m.namespace}: ${s}
`
307 | return html
308 | })
309 | .join('') +
310 | '\n
'
311 | }
312 | }
313 | })
314 | .then(() => cy.wrap({ messages }, { log: false }))
315 | } else {
316 | return cy.wrap({ messages }, { log: false })
317 | }
318 | }
319 |
320 | const normalize = (type: string, namespace: string | null = null): string => {
321 | let normalized = type.replace('.', '-')
322 | if (namespace) {
323 | namespace = namespace.replace('.', '-')
324 | normalized += `-${namespace}`
325 | }
326 | return normalized
327 | }
328 |
329 | const addOnClickFilter = (filterId: string): void => {
330 | // @ts-ignore
331 | const doc = cy.state('document')
332 | doc.getElementById(`check-${filterId}`).onclick = () => {
333 | const checkbox = doc.getElementById(`check-${filterId}`)
334 | const elements = doc.getElementsByClassName(checkbox.value)
335 | for (let log of elements) {
336 | log.style.display = checkbox.checked ? 'block' : 'none'
337 | }
338 | }
339 | }
340 |
341 | const getContainer = () => {
342 | // @ts-ignore
343 | const doc: Document = cy.state('document')
344 | // @ts-ignore
345 | const win: Window = cy.state('window')
346 | let container = doc.querySelector