Build a Chainable Rectangle Manipulation API
Company: Robinhood
Role: Frontend Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Technical Screen
## Build a Chainable Rectangle Manipulation API
You are given a page containing a 3-by-3 grid of rectangle elements and a JavaScript class whose methods are empty. Implement a fluent API that can select a rectangle, change its color, wait, and shift it horizontally while preserving the order written in the chain.
Representative usage:
```javascript
const rectangles = new RectanglesClass(document);
rectangles
.selectById(13)
.color("red")
.afterDelay(500)
.shiftByPx(10)
.color("green");
await rectangles.finished();
```
Assume each rectangle has a unique `data-rectangle-id` attribute. `shiftByPx(delta)` adds `delta` to that rectangle's accumulated horizontal shift. `finished()` is a test hook that returns a promise for all actions appended so far.
### Constraints & Assumptions
- `selectById`, `color`, `afterDelay`, and `shiftByPx` must return the same class instance immediately so calls remain chainable.
- Actions execute in chain order. The delay pauses later actions in this chain but does not block the browser's main thread.
- A later `selectById` changes the target only when that queued selection step executes.
- A missing rectangle or an action with no selected rectangle rejects the chain; later queued actions do not run.
- Separate class instances must not share queue or selection state.
### Clarifying Questions to Ask
- Should a failed chain be reusable, or should the caller create a new instance?
- May two independent chains manipulate the same rectangle, and if so, which ordering guarantee is expected between them?
- Should shifting preserve other CSS transforms, or may this exercise own the element's horizontal transform?
### Part 1 — Define the Fluent State
Choose the minimal instance state needed to track the selected rectangle, accumulated shifts, and unfinished work. Explain why every mutating method returns synchronously even though its action may run later.
#### What This Part Should Cover
- Per-instance selection and promise-tail state.
- A small helper that appends one action and returns `this`.
- Selection evaluated in queue order rather than at method-call time.
```hint Distinguish building from running
Each method call records one future step; the returned object and the eventual completion value serve different purposes.
```
### Part 2 — Preserve Delay and Mutation Order
Implement the four chainable methods so the example turns the rectangle red, waits about 500 milliseconds, shifts it, and only then turns it green.
#### What This Part Should Cover
- Promise composition or another non-blocking serial queue.
- A delay represented as a queued promise, not an independent timer around one mutation.
- DOM reads and writes performed when their step reaches the head of the queue.
```hint Attach every step to one tail
A timer that is launched separately can finish after a later color call; make it part of the same dependency chain.
```
### Part 3 — Handle Errors and Make It Testable
Show how `finished()` reports success or failure. Describe tests for immediate actions, delayed order, repeated shifts, changing selections, missing elements, and isolation between instances.
#### What This Part Should Cover
- Rejection propagation through later queued work.
- Deterministic tests using a short or fake timer and observable DOM state.
- A documented policy for whether an instance can recover after failure.
```hint Observe intermediate state
The strongest delay test checks the color before the timer resolves as well as the final state afterward.
```
### What a Strong Answer Covers
- A genuinely chainable API whose asynchronous execution order matches source order.
- DOM mutations applied to the selection active at each queued step.
- Non-blocking delay handling, cumulative movement, explicit failures, and instance isolation.
- Tests that would catch detached timers, eager selection, and swallowed promise rejections.
### Follow-up Questions
1. How would you add cancellation without leaving half of a chain running?
2. What changes if several chains must share one global ordering for a rectangle?
3. How would you preserve an element's existing rotate or scale transform while shifting it?
4. How would you expose progress without allowing callers to mutate the internal queue?
Quick Answer: Build a chainable JavaScript API that selects page rectangles, changes color, waits, and shifts them in the order written. Examine fluent state, ordered asynchronous effects, chain error behavior, DOM isolation, cancellation choices, and deterministic testing.