DEV Community

Javeed Shaik
Javeed Shaik

Posted on

Three times this week a tool said it worked, and it had not

Three times this week a tool told me it had done something, and it had not. Not one of them threw an error. Each looked exactly like success, and each was caught only because I went and checked the actual artefact afterwards.

I maintain a set of calculators and a small formula library, so most of my week is arithmetic and DOM. None of these bugs were exotic. That is the point — they are the ordinary kind, the kind that ships.

1. Grepping HTML for a word is not reading a page

I needed to know whether a set of pages had been updated to a new standard. The obvious move: fetch each page, search the HTML for the marker string.

curl -s "$url" | grep -c "0.55"
Enter fullscreen mode Exit fullscreen mode

Twelve of them came back positive. Great — except several of those pages plainly did not mention the standard anywhere in their text.

0.55 was matching inline CSS. opacity: 0.55, rgba(0,0,0,0.55), a transition duration. My "is this page updated" check was counting stylesheet values.

Worse, the failure was silent and confident. I did not get an error or an empty result — I got a plausible number that led to a wrong conclusion, and the conclusion was about to go out in an email to someone who would check it.

The same class of bug had bitten me a month earlier, testing whether an account was verified with 'verified' in html.lower(). That matched the word "verified" in unrelated page furniture and returned true for everything.

What actually works: match a structural marker, not a bare string — a class name, an element, an attribute:

'badge-verified' in html          # a class the page only emits when true
Enter fullscreen mode Exit fullscreen mode

Or skip the HTML entirely and read the rendered text. Raw HTML contains CSS, JSON blobs, script bodies, nav boilerplate and comments. Any word or number you search for lives in all of them.

2. Setting .value is not filling in a form

I needed to update a bio on a site built with a modern JS framework. Set the textarea's value, click save, done:

textarea.value = NEW_BIO;
saveButton.click();
Enter fullscreen mode Exit fullscreen mode

The tool reported success. The editor showed my new text. I clicked save. The page said it saved.

The live profile still had the old bio.

Assigning to .value mutates the DOM node, but React keeps its own copy of the state and never notices. On submit it serialises its state — the old value — and posts that. Nothing errors, because from the framework's perspective nothing happened.

The fix is to go through the native setter so the framework's synthetic onChange actually fires:

const setter = Object.getOwnPropertyDescriptor(
  window.HTMLTextAreaElement.prototype, 'value'
).set;
setter.call(textarea, NEW_BIO);
textarea.dispatchEvent(new Event('input',  { bubbles: true }));
textarea.dispatchEvent(new Event('change', { bubbles: true }));
Enter fullscreen mode Exit fullscreen mode

React patches value on the element instance, so calling the prototype's setter writes the real value underneath and the subsequent input event makes React pick it up.

I have now hit this on four different sites. Every time, the tooling said it worked.

3. A backgrounded tab gets zero animation frames

This one was a phantom. I was testing a calculator whose result counts up to its final value with a small requestAnimationFrame tween. Every automated check reported the headline number as 0.0. The supporting rows underneath were all correct. It looked like a real bug in exactly one code path.

It was not a bug. The tab was not visible, and browsers do not run requestAnimationFrame callbacks in a backgrounded tab. The tween's first frame fires synchronously — at t = 0, so it renders the starting value — and the next frame never comes. The readout freezes at zero and looks broken.

document.visibilityState  // "hidden"  <- that was the whole story
Enter fullscreen mode Exit fullscreen mode

My next move made it much worse. I patched requestAnimationFrame to a setTimeout so the tween would complete. Something else on the page ran a perpetual animation loop, that loop was now a tight timer loop, and the renderer froze hard enough to need a reload.

The lesson is narrower than "don't patch globals": if a value only renders during animation, do not try to force the animation. Read something that is not animated. The un-tweened rows were right there, computed by the same function, and they told me the arithmetic was fine.

The thing they have in common

None of these were logic errors. In all three, the code I wrote did exactly what I told it to. What failed was the signal I used to decide it had worked:

  • grep said "found it" — it had found something else
  • the DOM said "value set" — the framework disagreed
  • the readout said 0.0 — the environment could not render anything else

Every one of them was caught the same way: by looking at the finished artefact instead of the report about the artefact. Read the live page, not the editor. Read the rendered text, not the HTML. Read the value that is not being animated.

That sounds obvious written down. It is much less obvious at the moment you are doing it, because a tool returning success is genuinely good evidence most of the time — which is exactly what makes the exceptions expensive. A test that reports success while testing nothing is worse than no test at all.

The habit I have landed on is small: after any write I cannot see the effect of, fetch the thing fresh and assert on it. It costs a few seconds. All three of these would have shipped without it, and one of them nearly went out in an email.


The calculators this came out of are at Healthy Calculator Hub, and the formula library is health-fitness-formulas if you want the tested implementations.

Top comments (0)