src/components/dives/species-multi-select.test.ts
Rama main · réplica de lectura del repositorio original.
import { describe, expect, it } from "vitest";
import {
mapSpeciesResults,
parsePendingSpeciesId,
pendingSpeciesId,
} from "./species-multi-select";
import type {
SpeciesSuggestion,
SpeciesSuggestResponse,
} from "@/lib/api/species";
// Pinned to what the API actually sends. The WoRMS credit carries a markdown
// link and a plain `(CC BY)` run after it; `mapSpeciesResults` only dedupes
// these strings, so the shape matters here solely as a reminder that it is
// `Attribution` and not this function that has to cope with it.
const WORMS =
"[World Register of Marine Species](https://www.marinespecies.org) (CC BY)";
const WIKIDATA = "Wikidata (CC0)";
function result(overrides: Partial<SpeciesSuggestion> = {}) {
return {
aphia_id: 278400,
uuid: null,
scientific_name: "Amphiprion ocellaris",
common_name: "Ocellaris clownfish",
rank: "Species",
status: "accepted",
matched_name: null,
source: "wikidata",
attribution: WIKIDATA,
dive_count_at_sites: 0,
last_seen: null,
...overrides,
} as SpeciesSuggestion;
}
function response(
results: SpeciesSuggestion[],
has_more = false,
): SpeciesSuggestResponse {
return { results, has_more };
}
describe("pendingSpeciesId / parsePendingSpeciesId", () => {
it("round-trips an AphiaID", () => {
expect(parsePendingSpeciesId(pendingSpeciesId(278400))).toBe(278400);
});
it("reads a uuid as not pending, which is the branch that appends directly", () => {
expect(
parsePendingSpeciesId("0198c0de-1234-7000-8000-000000000001"),
).toBeNull();
});
it("refuses a prefixed id that isn't a positive integer", () => {
// Nothing produces these, but the parse decides whether a value goes into
// form state or into a resolve, and "NaN" must not become either.
expect(parsePendingSpeciesId("aphia:")).toBeNull();
expect(parsePendingSpeciesId("aphia:nope")).toBeNull();
expect(parsePendingSpeciesId("aphia:0")).toBeNull();
expect(parsePendingSpeciesId("aphia:1.5")).toBeNull();
});
});
describe("mapSpeciesResults", () => {
it("keys a catalog row by its uuid and an upstream one by its AphiaID", () => {
const mapped = mapSpeciesResults(
response([
result({ uuid: "species-1", aphia_id: 105792, source: "catalog" }),
result({ aphia_id: 278400 }),
]),
);
expect(mapped.items.map((item) => item.id)).toEqual([
"species-1",
"aphia:278400",
]);
});
it("shows the common name and hints with the binomial", () => {
const [item] = mapSpeciesResults(response([result()])).items;
expect(item.name).toBe("Ocellaris clownfish");
expect(item.hint).toBe("Amphiprion ocellaris");
});
it("hints with the rank when the binomial is already the name", () => {
// A row shown by its scientific name is disambiguated by nothing the name
// doesn't say - but "Genus" tells the diver they are about to log a genus.
const [item] = mapSpeciesResults(
response([
result({
scientific_name: "Amphiprion",
common_name: null,
rank: "Genus",
}),
]),
).items;
expect(item.name).toBe("Amphiprion");
expect(item.hint).toBe("Genus");
});
it("leaves the hint off entirely when there is no usable rank", () => {
// "unknown" is the API's placeholder for "no rank to report" - a Wikidata
// entity with no taxon-rank statement, or a WoRMS record that arrived
// without the field. A tail case now that the API reads the rank off the
// entity, and still a real one. "Manta americana, unknown" reads as a claim
// about the animal rather than about how much is known.
const [item] = mapSpeciesResults(
response([
result({
scientific_name: "Manta americana",
common_name: null,
rank: "unknown",
}),
]),
).items;
expect(item.name).toBe("Manta americana");
expect(item.hint).toBeUndefined();
});
it("still says what matched on a row with no usable rank", () => {
const [item] = mapSpeciesResults(
response([
result({
scientific_name: "Manta americana",
common_name: null,
rank: "unknown",
matched_name: "Cephalopterus manta",
}),
]),
).items;
expect(item.hint).toBe('matched "Cephalopterus manta"');
});
it("says which name matched when it isn't one on the row", () => {
// The accepted-taxon rule is invisible from the outside: searching "Manta
// birostris" returns a row reading *Mobula birostris*, and without this the
// diver has no way to tell it is the animal they typed.
const [item] = mapSpeciesResults(
response([
result({
aphia_id: 105857,
scientific_name: "Mobula birostris",
common_name: "Giant manta ray",
matched_name: "Manta birostris",
source: "worms",
attribution: WORMS,
}),
]),
).items;
expect(item.hint).toBe('Mobula birostris · matched "Manta birostris"');
});
it("stays quiet when the matched name is already on the row", () => {
expect(
mapSpeciesResults(
response([result({ matched_name: "ocellaris CLOWNFISH" })]),
).items[0].hint,
).toBe("Amphiprion ocellaris");
expect(
mapSpeciesResults(
response([result({ matched_name: "Amphiprion ocellaris" })]),
).items[0].hint,
).toBe("Amphiprion ocellaris");
});
it("counts the dives at the form's sites on a site row", () => {
// The count wins over the date: the row is first for its dives here, and
// carries a last-seen instant only because every logged species does.
const hints = mapSpeciesResults(
response([
result({
uuid: "species-1",
dive_count_at_sites: 3,
last_seen: "2026-03-12T09:00:00+02:00",
}),
result({ uuid: "species-2", aphia_id: 105857, dive_count_at_sites: 1 }),
]),
).items.map((item) => item.hint);
expect(hints).toEqual([
"Amphiprion ocellaris · 3 dives here",
"Amphiprion ocellaris · 1 dive here",
]);
});
it("dates a row the diver logged elsewhere, in that dive's own zone", () => {
// 01:30 at +10:00 is the 12th there and the 11th in UTC; the diver's day is
// the 12th.
const [item] = mapSpeciesResults(
response([
result({
uuid: "species-1",
last_seen: "2026-03-12T01:30:00+10:00",
matched_name: "Clown anemonefish",
}),
]),
).items;
expect(item.hint).toBe(
'Amphiprion ocellaris · last seen Mar 12, 2026 · matched "Clown anemonefish"',
);
});
it("returns summaries only for rows that are already catalog rows", () => {
// An upstream row has no uuid, so there is nothing a label map could key it
// on until a resolve gives it one.
const mapped = mapSpeciesResults(
response([result({ uuid: "species-1" }), result({ aphia_id: 105857 })]),
);
expect(mapped.summaries).toEqual([
{
uuid: "species-1",
scientific_name: "Amphiprion ocellaris",
common_name: "Ocellaris clownfish",
rank: "Species",
},
]);
});
it("dedupes the licence credits and carries has_more through", () => {
const mapped = mapSpeciesResults(
response(
[
result({ aphia_id: 1, attribution: WORMS, source: "worms" }),
result({ aphia_id: 2, attribution: WORMS, source: "worms" }),
result({ aphia_id: 3, attribution: WIKIDATA }),
],
true,
),
);
expect(mapped.attributions).toEqual([WORMS, WIKIDATA]);
expect(mapped.hasMore).toBe(true);
});
it("collapses results that would share a row id", () => {
// Two menu rows with the same React key are both a warning and a row that
// can't be excluded from the menu once it has been picked.
const mapped = mapSpeciesResults(
response([result(), result({ common_name: "Common clownfish" })]),
);
expect(mapped.items).toHaveLength(1);
expect(mapped.items[0].name).toBe("Ocellaris clownfish");
expect(mapped.results.get("aphia:278400")?.common_name).toBe(
"Ocellaris clownfish",
);
});
});