This will happen to you, probably in your first month. A test fails on the build server about once in ten runs. On your machine it passes every time. Someone re-runs the build, it goes green, and everyone moves on.
Do not move on. Selenium does not record video or traces for you, so your first job is to make sure a failure leaves evidence behind.
Step 1. Save a screenshot when a test fails. Add this to your teardown:
afterEach(async function () {
if (this.currentTest.state === 'failed') {
const image = await driver.takeScreenshot();
fs.writeFileSync(`screenshots/${this.currentTest.title}.png`, image, 'base64');
}
await driver.quit();
});
This single block will save you more time than anything else in this lecture. Without it, a CI failure is just a stack trace and a guess.
Step 2. Look at the screenshot and ask one question.
What is on this screen that I did not expect?
Nine times out of ten the answer is right there: a spinner still turning, an empty table, a cookie banner covering the button.
Step 3. Save the browser console too. Some failures come from a JavaScript error in the app:
const logs = await driver.manage().logs().get('browser');
console.log(logs.map((l) => l.message).join('\n'));
Step 4. Make it fail for you. Run the single test twenty or fifty times. If it still passes, slow the network in your browser settings. The build server is usually slower than your laptop, and slow is what makes timing bugs appear.
Step 5. Fix the cause, not the symptom. Three usual causes in Selenium:
- A
sleepor a wait that is too short. Replace it with an explicit wait for the real condition. - A stale element. You held a reference across a page change. Find the element again.
- Shared data. Another test left something behind. Give this test its own data.
What not to do: raise every timeout, or add retries and move on. Both hide the failure while the bug stays in the app.
Remember this: make failures leave evidence first. You cannot debug what you cannot see.
Reference: Troubleshooting.