In this post
- TikTok: the time picker that jumps by three
- X: the toolbar that changes height
- Meta: the click that opens a system dialog
- The pattern
- The three rules that survived
- 1. Never reuse a coordinate across calls
- 2. Verify in a call separate from the action
- 3. Wait for conditions, never for time
- What this has to do with building apps
- The honest scoreboard
After fixing the two YouTube bugs — covered in part 1 — we expected the rest to be routine. TikTok, X, Instagram, Facebook, Kwai and Reddit were left.
Every platform produced a new failure. And every new failure, looked at closely, was the same failure.
TikTok: the time picker that jumps by three
TikTok's scheduler does not accept a typed time. It is a scroll picker with behaviour nobody documents:
- clicking the last visible row of the hour column advances +3 hours
- the value actually selected is the middle row, not the row you clicked
- the dropdown's position on screen varies with available space
So you click "18" and select "15". Or "21". Depending on where the dropdown decided to open.
Video 15 got scheduled for 18:10 instead of 18:00. The exact reason: the verification had been issued in the same call as the click. The response described the state before the click took effect, and we read that response as confirmation.
Verifying and acting in one call is the same mistake as reading a value and using it after another thread wrote to it. Here the other thread is the UI.
X: the toolbar that changes height
On X the schedule button lives in the composer toolbar, which changes height in three situations:
| Composer state | Button position |
|---|---|
| One line of text | y = 312 |
| Two lines of text | y = 337 |
| Link preview card loaded | y = 625 |
Three different values, and the third only appears seconds later, once X finishes fetching the preview. A coordinate captured before the card loads points at the wrong place by the time the click happens.
And that is not all: the window resizes on its own between actions. Nothing that depends on absolute position survives that.
Meta: the click that opens a system dialog
The most interesting case was Meta's video editor. The visible upload button opens the operating system's native file dialog — outside the browser, and therefore out of reach of any page automation.
The fix was not clicking better. It was understanding what the button does: it calls click() on a <input type="file"> the application creates on the fly. With that element located, the file goes straight to it — no system dialog involved.
When a click opens a system window, page automation ends there. The answer is not to keep clicking: it is to find the element the click triggers.
The pattern
| Platform | What moves | What breaks |
|---|---|---|
| YouTube | Title field grows as you type | The description click lands in the title |
| TikTok | Dropdown opens up or down | Silently selects the wrong hour |
| X | Toolbar shifts when the preview loads | The schedule click misses |
| Meta | Upload element is created on click | The system dialog blocks everything |
| Grid changes after upload | "Next" moves | |
| Editor toggles markdown / rich text | Text lands in the wrong field |
Six interfaces, six different reasons, one problem: between the moment you read the screen and the moment you click, the screen changed.
The three rules that survived
1. Never reuse a coordinate across calls
A coordinate has a shelf life measured in milliseconds. Either you locate the element and click it, or you read its position in the same action that clicks. Storing (694, 337) for the next step is storing a pointer to freed memory.
2. Verify in a call separate from the action
This is the rule that cost the most to learn and paid the most. Checking and clicking together produces a reading of the past. Confirm, then act.
3. Wait for conditions, never for time
wait 3 seconds is a guess. wait until the footer says "Checks complete" is a contract. Every time the process depended on a fixed delay, it failed on a larger video or a slower connection.
- YouTube: wait for the footer text to change
- TikTok: the first upload attempt always fails with an anti-automation
guard; reloading and retrying works. That is a condition, not a delay.
- X: the schedule dialog is not in the accessibility tree for the first few
seconds even though it is visibly open; repeating the query resolves it
What this has to do with building apps
Everything, which is why this post sits in the engineering track.
The same three rules describe UI testing, and they explain why end-to-end tests have a reputation for flakiness. A test that waits on time, stores coordinates and asserts on state it does not control is flaky by construction — and the tooling takes the blame unfairly.
The short version, good for both:
Do not assert anything about a screen you did not just read. And do not read a screen in the same action that modifies it.
The honest scoreboard
None of those is 100%, and the reasons are not technical: TikTok only allows scheduling 30 days ahead, and Meta enforces an upload rate limit that shows up as an upload stuck at 0% — no error, no warning, no network request. Waiting is the only fix.
That is engineering information too. Half the work of publishing at scale is not publishing: it is finding where each platform puts the brake and stopping hitting it.
In part 3: a post that looked published, appeared on our profile, and existed for nobody else.
Frequently asked questions
Why not always use element selectors instead of coordinates?
Because it is not always possible. Some components only respond to a real pointer event — clicking the element merely focuses it. In those cases the rule is to read the element's position at click time, never to reuse a coordinate captured earlier.
Which rule finally stabilised the process?
Separating verification from action. While we checked and clicked in the same call, the check described a state that no longer existed when the click landed. Verify, then act in the next call.
Does this apply to UI testing in general?
It does, and it is the same lesson as end-to-end testing: assert on state you control, wait for conditions rather than time, and never carry screen positions between steps. The difference here is that the UI belongs to someone else and changes without notice.