英文:
How to log only to node terminal inside cy.intercept?
问题
I want to log messages in the Cypress terminal only, so that they don't appear in the screenshots in the Cypress browser Command logs and can be viewed in the CI logs instead.
我想仅在Cypress终端中记录消息,以便它们不会出现在Cypress浏览器命令日志的截图中,而是可以在CI日志中查看。
I tried using Cypress.log(), but it logs messages to both the terminal and the browser spec, which is not what I want.
我尝试使用Cypress.log(),但它将消息记录到终端和浏览器规范中,这不是我想要的。
This is currently what I'm using but it spams the screenshots with RequestId logs (I only want it in CI terminal)
这是我目前使用的方式,但它会在截图中生成大量RequestId日志(我只想在CI终端中看到它):
cy.intercept({ url: API_HOST + '/api/**', middleware: true }, req => {
const requestId =
// Apollo lower cases the headers before sending the request
req.headers['x-request-id'] || req.headers['X-Request-Id'];
if (requestId) {
Cypress.log({
name: 'RequestID:',
message: requestId,
});
}
});
});
I also tried using console.log() inside cy.intercept, but it only logs to the Chrome console and not the Node.js terminal, so the messages are not shown anywhere. (I noticed that if I console.log within the application elsewhere it added the console.log into the Node.js Terminal but specifically inside cy.intercept it doesn't log it)
我还尝试在cy.intercept内部使用console.log(),但它只记录到Chrome控制台,而不是Node.js终端,因此消息在任何地方都不会显示。 (我注意到,如果我在应用程序的其他地方使用console.log,则会将console.log添加到Node.js终端,但在cy.intercept内部,它不会记录。)
Finally, I tried using cy.task,
最后,我尝试使用cy.task:
cy.intercept({ url: API_HOST + '/api/**', middleware: true }, req => {
const requestId =
// Apollo lower cases the headers before sending the request
req.headers['x-request-id'] || req.headers['X-Request-Id'];
if (requestId) {
cy.task('log', `RequestId: ${requestId}`);
}
});
});
But I received this error
但我收到了这个错误:
Cypress检测到您从命令返回了一个promise,并在该promise中调用了一个或多个cy命令。
The command that returned the promise was:
返回promise的命令是:
> cy.visit()
The cy command you invoked inside the promise was:
您在promise内部调用的cy命令是:
> cy.task()
Because Cypress commands are already promise-like, you don't need to wrap them or return your own promise.
由于Cypress命令已经类似于promise,您不需要包装它们或返回自己的promise。
Cypress will resolve your command with whatever the final Cypress command yields.
Cypress将使用最终的Cypress命令产生的内容来解决您的命令。
The reason this is an error instead of a warning is because Cypress internally queues commands serially whereas Promises execute as soon as they are invoked. Attempting to reconcile this would prevent Cypress from ever resolving.
之所以将其作为错误而不是警告是因为Cypress在内部以串行方式排队命令,而Promise在调用时立即执行。试图调和这一点会阻止Cypress永远不会解决。
How can I log messages in the Cypress terminal only without them appearing in the cypress browser command logs?
如何在Cypress终端中记录消息,而不使它们出现在Cypress浏览器命令日志中?
英文:
I want to log messages in the Cypress terminal only, so that they don't appear in the screenshots in the Cypress browser Command logs and can be viewed in the CI logs instead.
I tried using Cypress.log(), but it logs messages to both the terminal and the browser spec, which is not what I want.
This is currently what I'm using but it spams the screenshots with RequestId logs (I only want it in CI terminal)
beforeEach(() => {
cy.intercept({ url: API_HOST + '/api/**', middleware: true }, req => {
const requestId =
// Apollo lower cases the headers before sending the request
req.headers['x-request-id'] || req.headers['X-Request-Id'];
if (requestId) {
Cypress.log({
name: 'RequestID:',
message: requestId,
});
}
});
});
I also tried using console.log() inside cy.intercept, but it only logs to the Chrome console and not the Node.js terminal, so the messages are not shown anywhere. (I noticed that if I console.log within the application elsewhere it added the console.log into the Node.js Terminal but specifically inside cy.intercept it doesn't log it)
Finally, I tried using cy.task,
beforeEach(() => {
cy.intercept({ url: API_HOST + '/api/**', middleware: true }, req => {
const requestId =
// Apollo lower cases the headers before sending the request
req.headers['x-request-id'] || req.headers['X-Request-Id'];
if (requestId) {
cy.task('log', `RequestId: ${requestId}`);
}
});
});
But I received this error
The command that returned the promise was:
> cy.visit()
The cy command you invoked inside the promise was:
> cy.task()
Because Cypress commands are already promise-like, you don't need to wrap them or return your own promise.
Cypress will resolve your command with whatever the final Cypress command yields.
The reason this is an error instead of a warning is because Cypress internally queues commands serially whereas Promises execute as soon as they are invoked. Attempting to reconcile this would prevent Cypress from ever resolving.".
How can I log messages in the Cypress terminal only without them appearing in the cypress browser command logs?
答案1
得分: 10
I'll provide the translated code section without additional content:
问题是 cy.intercept()
回调函数是异步事件处理程序,而 Cypress 命令也可以触发事件。
为了避免潜在的级联事件,Cypress 不允许在事件处理程序内部触发命令。
但你可以记录事件并在测试流程中以后处理它。
例如,当 cy.intercept()
需要记录到终端时,可以在 Cypress.env()
中记录它。
cy.intercept({ url: '**/api/*', middleware: true }, (req) => {
const headerValue = req.headers['x-custom-headers'];
if (headerValue) {
const terminalLogs = Cypress.env('terminalLogs') || [];
terminalLogs.push(`requestId: ${headerValue}`);
Cypress.env('terminalLogs', terminalLogs);
}
}, { log: false });
然后,在测试适当的时候,例如 afterEach()
,
afterEach(() => {
const terminalLogs = Cypress.env('terminalLogs') || [];
terminalLogs.forEach(log => {
cy.task('log', log, { log: false });
});
});
cypress.config.ts
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('task', {
log(message) {
console.log(message);
return null;
},
});
return config;
}
},
});
在不等待 cy.wait('@intercept-alias')
的情况下
如果你决定不使用 cy.wait('@intercept-alias')
,那么测试可能会在拦截回调触发之前完成并关闭。
如果数据捕获在request
回调上,那可能没问题。
但在reponse
一侧,捕获可能会被网络延迟,你应该确保Cypress.env('terminalLogs')
只在响应被处理后才触发,例如在afterEach()
中。
英文:
The problem is that cy.intercept()
callback functions are asynchronous event handlers, and Cypress commands can also trigger events.
To avoid a potential cascading event, Cypress prohibits firing commands inside an event handler.
But you can record the event and act on it later in the test flow.
For instance, when a cy.intercept()
needs to log to terminal, record it in a Cypress.env()
.
cy.intercept({ url: '**/api/*', middleware: true, }, (req) => {
const headerValue = req.headers['x-custom-headers']
if (headerValue) {
const terminalLogs = Cypress.env('terminalLogs') || []
terminalLogs.push(`requestId: ${headerValue}`)
Cypress.env('terminalLogs', terminalLogs)
}
}, {log: false})
Then, at some point appropriate to the test, for example afterEach()
,
afterEach(() => {
const terminalLogs = Cypress.env('terminalLogs') || []
terminalLogs.forEach(log => {
cy.task('log', log, {log:false})
})
})
cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('task', {
log(message) {
console.log(message);
return null;
},
})
return config
}
},
})
Without waiting for cy.wait('@intercept-alias')
If you decide not to use a cy.wait('@intercept-alias')
, the test can finish and shut down before the intercept has fired it's callback.
If the data capture is on the request
callback, it's probably ok.
But on the reponse
side, the capture can be delayed by the network and you should make sure Cypress.env('terminalLogs')
is fired only after the response has been handled, e.g in an afterEach()
.
答案2
得分: 6
If you are having problems calling cy.task()
inside the intercept handler, try using the alias/wait pattern and logging from the wait callback.
This is the general pattern:
cy.intercept(url, () => {
// can't use task here
}).as('logSomething')
// trigger fetch
// wait for intercept
cy.wait('@logSomething').then(interception => {
// all request & response properties available here
cy.task('log', interception.response.body.id)
})
This is my code calling cy.task() without the same error
cy.intercept({ url: '**/api/*', middleware: true, }, (req) => {
const headerValue = req.headers['x-custom-headers']
if (headerValue) {
cy.task('log', `requestId: ${headerValue}`)
}
}, {log: false}).as('middleware')
// a non-middleware intercept, also fires the log task
cy.intercept('**/api/*', (req) => {
cy.task('log', 'something else')
req.reply({})
})
// trigger a fetch from the app window
cy.window().then(win => win.fetch('http://some-domain/api/1', {
headers: {
"x-custom-headers": "123",
},
}))
cy.wait('@middleware') // wait on the middleware
I'm waiting on the middleware intercept just to make sure the test runs long enough, but that wouldn't affect the error you are getting.
英文:
If you are having problems calling cy.task()
inside the intercept handler, try using the alias/wait pattern and logging from the wait callback.
This is the general pattern:
cy.intercept(url, () => {
// can't use task here
}).as('logSomething')
// trigger fetch
// wait for intercept
cy.wait('@logSomething').then(interception => {
// all request & response properties available here
cy.task('log', interception.response.body.id)
})
This is my code calling cy.task() without the same error
cy.intercept({ url: '**/api/*', middleware: true, }, (req) => {
const headerValue = req.headers['x-custom-headers']
if (headerValue) {
cy.task('log', `requestId: ${headerValue}`)
}
}, {log: false}).as('middleware')
// a non-middleware intercept, also fires the log task
cy.intercept('**/api/*', (req) => {
cy.task('log', 'something else')
req.reply({})
})
// trigger a fetch from the app window
cy.window().then(win => win.fetch('http://some-domain/api/1', {
headers: {
"x-custom-headers": "123",
},
}))
cy.wait('@middleware') // wait on the middleware
I'm waiting on the middleware intercept just to make sure the test runs long enough, but that wouldn't affect the error you are getting.
答案3
得分: 1
// cypress.config.ts
setupNodeEvents(on, config) {
on('task', {
log(message) {
console.log(`\n${message}\n`);
return null;
},
});
return config;
},
然后在你的规范文件中:
// your.spec.ts
cy.task('log', ' ->>>>>>>> test terminal log <<<<<<<-');
英文:
You can do it using the cy.task
// cypress.config.ts
setupNodeEvents(on, config) {
on('task', {
log(message) {
console.log(`\n${message}\n`);
return null;
},
});
return config;
},
then in your spec file:
// your.spec.ts
cy.task('log', ' ->>>>>>>> test terminal log <<<<<<<-');
答案4
得分: 0
我想提供一个对我有用的另一个选项的答案。
由于我试图记录所有请求的标头,我注意到 cypress-terminal-report
有一个选项,可以通过这些选项在终端中记录所有请求的标头(我无法找到一种过滤掉不必要的响应标头和请求正文的方法,但这比以前好了,终端中的附加信息也没有坏处)
require('cypress-terminal-report/src/installLogsCollector')({
xhr: {
printHeaderData: true,
printRequestData: true,
},
});
英文:
I wanted to provide an answer on another option that worked for me.
Since I was trying to log the headers for all the request I noticed that cypress-terminal-report
had an option
to log all the request headers in the terminal via these option (I couldn't figure out a way to filter out the unnecessary response header and request body but this got me into a better spot than I was before and additional info doesn't hurt in the terminal)
require('cypress-terminal-report/src/installLogsCollector')({
xhr: {
printHeaderData: true,
printRequestData: true,
},
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论