import type { ReactNode } from 'react'; import { afterEach, describe, expect, test } from 'bun:test'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { MemoryRouter, useLocation } from 'react-router'; import { render, screen, waitFor } from '@testing-library/react'; import { ServersView } from './ServersView'; import { offscaleRegisterPath } from './shared'; /** * Whether "Register a server" opens the form — from every place that offers it. * * The bug this pins was reported as "I click register a server and nothing happens", and nothing was * broken: three controls carry that label and the one in the server picker was a plain link to the Servers * section. `/offscale` and `/offscale/servers` render the same view, so from the section you were already * on it changed the URL and nothing else — a control that looks like the primary action and is a no-op. * * So the test is about the link's DESTINATION doing something, which is why it renders the view at that * destination rather than clicking the picker: the picker lives in another panel, and what has to be true * is that arriving there opens the form. * * `fetch` is stubbed rather than `mock.module('hooks/useClient')` — Bun has one module registry per run, so * that mock is process-wide and permanent, and other files already own it. */ const realFetch = globalThis.fetch; afterEach(() => { globalThis.fetch = realFetch; }); const stubServers = (servers: unknown[] = []) => { globalThis.fetch = (async () => new Response(JSON.stringify({ servers }), { status: 200, headers: { 'content-type': 'application/json' }, })) as unknown as typeof fetch; }; /** Reports the live URL, so "the flag was consumed" is an assertion rather than an inference. */ const Search = () => {useLocation().search}; const Wrap = ({ at, children }: { at: string; children: ReactNode }) => ( {children} ); const mountAt = (at: string) => render( , ); const formHeading = () => screen.queryByText('Register a Headscale server'); const search = () => screen.getByTestId('search').textContent; describe('ServersView registration form', () => { test('the picker link lands with the form already open', async () => { stubServers([]); mountAt(offscaleRegisterPath()); await waitFor(() => expect(formHeading()).not.toBeNull()); }); test('the flag is consumed, so clicking the picker link again still works', async () => { stubServers([]); mountAt(offscaleRegisterPath()); // Left in the URL, a second click on the picker would be a navigation to the URL already showing — // which is the original bug, restored. await waitFor(() => expect(search()).toBe('')); }); test('without the flag the form stays closed', async () => { stubServers([]); mountAt('/offscale/servers'); // The empty state has to have rendered before "no form" means anything. await waitFor(() => expect(screen.queryByText('No Headscale servers yet')).not.toBeNull()); expect(formHeading()).toBeNull(); }); });