DEV Community

Daniel Pertu
Daniel Pertu

Posted on

Our employer pages now name the tests, and three different sentences say who told us

A candidate interviewing at UBS wants to know which tasks UBS sends, not which tasks the vendor sells. We have 180 employer guides and until this week none of them answered that, because answering it honestly is hard: employers almost never publish the names of the tests they use.

So the new module is small on purpose. 21 lineups, covering 21 of the 180 employer pages, 10 providers, 50 named tasks. The component returns null for the other 159.

export function EmployerTaskLineups({ slug, employer }: { slug: string; employer: string }) {
  const lineups = taskLineupsForEmployer(slug);
  if (lineups.length === 0) return null;
  ...
Enter fullscreen mode Exit fullscreen mode

One field decides the heading and the attribution

The interesting part of the type is a three-value union:

/**
 * - `named`: the employer or the vendor names the tasks.
 * - `described`: the employer describes each task in detail without naming it,
 *   and the descriptions match the vendor's own task descriptions.
 * - `reported`: a third party (candidate reports or a prep site) names them.
 */
basis: 'named' | 'described' | 'reported';
Enter fullscreen mode Exit fullscreen mode

It is not metadata. It changes what the page says, in two places. The heading:

{lineup.basis === 'reported'
  ? `${provider} tasks reported at ${employer}`
  : `${provider} tasks ${employer} uses`}
Enter fullscreen mode Exit fullscreen mode

and the line under the list:

const lead =
  lineup.basis === 'reported' ? 'Reported by '
  : lineup.basis === 'described' ? 'Described, not named, in the '
  : 'Source: ';
Enter fullscreen mode Exit fullscreen mode

Of the 21 lineups, 19 are named, one is reported and one is described. The described one is the reason the field exists at all. Procter and Gamble's careers material walks through three interactive assessments in detail without ever naming them: fill the gaps in equations, pick the branch that reorders the symbols, recall the dot positions. Those are three named tasks from a vendor we cover, and matching the descriptions is a judgement call, so the page says it is a judgement call. The heading still says P&G uses them. The line underneath says "Described, not named, in the P&G Interactive Assessment guide, checked 2026".

Compare the single reported entry, where it reads "Reported by WikiJob, 2025", and the heading changes to "Arctic Shores tasks reported at PwC". Same component, different claim, different sentence.

"checked 2026" rather than a year we made up

Every lineup carries a source with a year, and the year means different things:

source: {
  url: string;
  label: string;
  /** Year the source dates from, or the year it was checked live if undated. */
  year: number;
  undated?: boolean;
};
Enter fullscreen mode Exit fullscreen mode

18 of the 21 sources are undated careers pages, so the rendered line says checked 2026, not 2026. One line of code, and it stops a careers page with no date on it from acquiring one in our markup.

The other number worth knowing: those 21 sources sit on only 13 hosts, and nine of the 21 come from one vendor's own customer case studies. The pattern across the whole sweep is that the vendor will tell you which employers it sells to long before the employer will tell you which vendor it buys from.

The list degrades in two steps and looks the same either way

Each task optionally carries the id of our practice game for it, and the name becomes a link to that test's page if one exists:

const page = task.gameId ? testPageForGame(task.gameId) : undefined;
Enter fullscreen mode Exit fullscreen mode

Of the 50 tasks, 31 render as links. Ten carry no game id at all, because the employer names a stage we have no practice for and leaving it out would misrepresent their process. Another nine do have a practice game but no per-test page yet, so testPageForGame returns undefined and they render as plain text. Eight of those nine are the same aptitude test, reused through a shared constant across eight employers:

const GIA: LineupTask = { name: 'Aptitude assessment (GIA)', gameId: 'thm-gia' };
Enter fullscreen mode Exit fullscreen mode

The reader cannot tell the two cases apart, which is the point. A task you cannot practise with us still belongs on the list.

The guard test generates one case per lineup

for (const lineup of TASK_LINEUPS) {
  const label = `${lineup.employerSlug} / ${lineup.provider}`;

  it(`${label}: belongs to an existing employer page`, () => {
    const page = path.join(process.cwd(), 'app', 'employers', lineup.employerSlug, 'page.tsx');
    expect(existsSync(page), `missing ${page}`).toBe(true);
  });
  ...
Enter fullscreen mode Exit fullscreen mode

A loop that emits an it per entry rather than one it with a loop inside costs nothing and buys you a failure message that names the employer. The assertions are the ones a sourced claim needs: an https source with a non-empty label, a year between 2015 and this year, at least one task, no em dashes anywhere in the entry, at most one lineup per employer and provider, and the one that catches real mistakes:

const game = GAMES.get(task.gameId);
expect(game, `unknown game id ${task.gameId}`).toBeDefined();
expect(game?.provider).toBe(lineup.provider);
Enter fullscreen mode Exit fullscreen mode

A task cannot point at a game belonging to a different vendor. That is exactly the slip you make at 1am when two providers both sell something called a numerical reasoning test.

One last detail, since every list item here is a tappable link on a phone: the anchors carry min-h-11 sm:min-h-0, so they are 44px targets on a phone and ordinary inline text on a desktop.

See it

Open the UBS guide and you get four named tasks with "Source: UBS early careers page, checked 2026" under them. PwC says "reported at" in the heading and "Reported by WikiJob, 2025" underneath. Procter and Gamble is the described one, and its list is an <ol> rather than a <ul>, because that source gives the tasks in the order candidates sit them.

Then open Amazon and search for "Source:". Nothing, because there is no lineup for it and the section does not render. That is 159 pages waiting for somebody to publish something.

The full set is linked from /employers.

Worth contrasting with the other content type we shipped the same day: on our per-company interview question pages, every question carries the URL it was reported at and the page renders none of them. Here the source is the first thing under the list. The difference is what the claim is about. "These are the questions people report" is a claim about an aggregate. "This employer uses these tests" is a claim about a named third party's hiring process, and a reader deserves to see who said it before deciding whether to believe us.

Top comments (0)