← Files Biological Sequence & Alignment ViewerARCHIVED FILE

src/msa/preview.test.tsx

54.5 KB · Sep 30, 2026 · 23:01 UTC

↓ Download file

import {
  act,
  fireEvent,
  render,
  screen,
  waitFor,
  within,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import type { ReactElement } from "react";
import { IntlProvider } from "react-intl"; // oxlint-disable-line no-restricted-imports
import { describe, expect, it, vi } from "vitest";

import { MsaPreview } from "./msa-rich-viewer";
import * as artifactStateKey from "../artifact-state-key";
import { getSequenceResidueStyle } from "../sequence/sequence-palette";
import { WorkbenchToolController } from "../ui/workbench-tool-controller";
import { WorkbenchToolControllerContext } from "../ui/workbench-tools";
import type {
  QueuedSequenceViewerCommand,
  SequenceViewerCommand,
  SequenceViewerCommandResult,
} from "../viewer-commands";

function renderPreview(
  contents: string,
  filePath = "/tmp/alignment.afa",
): ReturnType<typeof render> {
  return renderWithMsaProviders(
    <MsaPreview className="h-full" contents={contents} filePath={filePath} />,
  );
}

function renderWithMsaProviders(ui: ReactElement): ReturnType<typeof render> {
  return render(ui, {
    wrapper: ({ children }): ReactElement => (
      <IntlProvider locale="en">{children}</IntlProvider>
    ),
  });
}

async function waitForViewer(): Promise<HTMLElement> {
  return screen.findByLabelText(
    "Interactive multiple sequence alignment viewer",
  );
}

function generateAlignment(rowCount: number, columnCount: number): string {
  const sequence = "ACGT"
    .repeat(Math.ceil(columnCount / 4))
    .slice(0, columnCount);
  return Array.from({ length: rowCount }, (_, index) =>
    [`>seq-${index + 1}`, sequence].join("\n"),
  ).join("\n");
}

describe("MsaPreview", () => {
  it("controls metric tracks, help, row order, and row selection without opening an inspector", async () => {
    const contents = [">alpha", "ACGT", ">beta", "ACGA"].join("\n");
    const onCommandResult = vi.fn();
    const updateModelContext = vi.fn();
    const view = renderWithMsaProviders(
      <MsaPreview
        command={alignmentCommand({
          action: "set_alignment_view_options",
          enabledMetricTracks: ["identity", "sequence-logo"],
          rowSortDirection: "desc",
          rowSortKey: "label",
          showSequenceLogoHelp: true,
        })}
        contents={contents}
        onCommandResult={onCommandResult}
        updateModelContext={updateModelContext}
      />,
    );
    await waitForViewer();
    await waitFor(() =>
      expect(onCommandResult).toHaveBeenCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: true,
          state: expect.objectContaining({
            enabledMetricTracks: ["identity", "sequence-logo"],
            rowSortDirection: "desc",
            rowSortKey: "label",
            rowSortScope: "row-manager",
            showSequenceLogoHelp: true,
          }),
        }),
      ),
    );
    expect(screen.getByRole("button", { name: "Rows" })).toHaveAttribute(
      "aria-expanded",
      "false",
    );
    expect(screen.getByLabelText(/Sequence logo \(mix/)).toBeChecked();
    expect(
      screen.getByText("What does Sequence logo show?").closest("details"),
    ).toHaveAttribute("open");
    await waitFor(() =>
      expect(updateModelContext).toHaveBeenLastCalledWith(
        expect.objectContaining({
          structuredContent: expect.objectContaining({
            display: expect.objectContaining({
              enabledMetricTracks: ["identity", "sequence-logo"],
              rowSortDirection: "desc",
              rowSortKey: "label",
              showSequenceLogoHelp: true,
            }),
          }),
        }),
      ),
    );

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "select_alignment_rows", rows: ["alpha"] },
          2,
        )}
        contents={contents}
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() =>
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: true,
          state: { selectedRowCount: 1, selectedRowIds: ["alpha"] },
        }),
      ),
    );
    await userEvent.click(screen.getByRole("button", { name: "Rows" }));
    expect(screen.getByLabelText("Select row alpha")).toBeChecked();
    const rows = screen.getByLabelText("MSA row visibility controls");
    expect(
      within(rows).getAllByRole("checkbox", { name: /Select row/ })[0],
    ).toHaveAccessibleName("Select row beta");
  });

  it("rejects unavailable metric tracks and ambiguous row selections without partial changes", async () => {
    const contents = [">dup", "ACGT", ">dup", "ACGA"].join("\n");
    const onCommandResult = vi.fn();
    const view = renderWithMsaProviders(
      <MsaPreview
        command={alignmentCommand({
          action: "set_alignment_view_options",
          colorMode: "residue",
          enabledMetricTracks: ["rna-structure"],
        })}
        contents={contents}
        onCommandResult={onCommandResult}
      />,
    );
    await waitForViewer();
    await waitFor(() =>
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: false,
          state: expect.objectContaining({
            unavailableMetricTracks: ["rna-structure"],
          }),
        }),
      ),
    );
    expect(screen.getByLabelText("MSA color mode")).toHaveValue("difference");

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "select_alignment_rows", rows: ["dup"] },
          2,
        )}
        contents={contents}
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() =>
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: false,
          state: { candidateRowIds: ["dup__1", "dup__2"] },
        }),
      ),
    );
    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "select_alignment_rows", rows: ["dup__2"] },
          3,
        )}
        contents={contents}
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() =>
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: true,
          state: { selectedRowCount: 1, selectedRowIds: ["dup__2"] },
        }),
      ),
    );
    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "select_alignment_rows", rows: ["dup__1", "missing"] },
          4,
        )}
        contents={contents}
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() =>
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: false,
          state: { missingRows: ["missing"] },
        }),
      ),
    );
    expect(
      screen
        .getAllByLabelText(/^Select row/)
        .map((input) => (input as HTMLInputElement).checked),
    ).toEqual([false, true]);
    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "select_alignment_rows", rows: [] },
          5,
        )}
        contents={contents}
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() =>
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: true,
          state: { selectedRowCount: 0, selectedRowIds: [] },
        }),
      ),
    );
  });

  it.each(["ncbi-nucleic-acid", "muted-nucleic-acid", "neutral"] as const)(
    "round-trips %s and display options in saved alignment sessions",
    async (residuePalette) => {
      const contents = [">alpha", "ACGT", ">beta", "ACGA"].join("\n");
      let savedSession: string | undefined;
      const onCommandResult = vi.fn(
        (
          _: QueuedSequenceViewerCommand,
          result: SequenceViewerCommandResult,
        ) => {
          if (typeof result.state?.session === "string")
            savedSession = result.state.session;
        },
      );
      const view = renderWithMsaProviders(
        <MsaPreview
          command={alignmentCommand({
            action: "set_alignment_view_options",
            colorMode: "residue",
            residuePalette,
            enabledMetricTracks: ["sequence-logo"],
            rowSortDirection: "desc",
            rowSortKey: "length",
            showSequenceLogoHelp: true,
          })}
          contents={contents}
          onCommandResult={onCommandResult}
        />,
      );
      await waitForViewer();
      await waitFor(() => expect(onCommandResult).toHaveBeenCalled());
      view.rerender(
        <MsaPreview
          command={alignmentCommand(
            { action: "save_session", name: "alignment-session" },
            2,
          )}
          contents={contents}
          onCommandResult={onCommandResult}
        />,
      );
      await waitFor(() => expect(savedSession).toBeDefined());
      if (savedSession == null)
        throw new Error("Expected a serialized alignment session.");
      view.rerender(
        <MsaPreview
          command={alignmentCommand({ action: "reset_alignment_view" }, 3)}
          contents={contents}
          onCommandResult={onCommandResult}
        />,
      );
      await waitFor(() =>
        expect(screen.getByLabelText(/Sequence logo \(mix/)).not.toBeChecked(),
      );
      view.rerender(
        <MsaPreview
          command={alignmentCommand(
            { action: "restore_session", session: savedSession },
            4,
          )}
          contents={contents}
          onCommandResult={onCommandResult}
        />,
      );
      await waitFor(() =>
        expect(onCommandResult).toHaveBeenLastCalledWith(
          expect.anything(),
          expect.objectContaining({
            applied: true,
            message: "Restored the saved Alignment workbench session.",
          }),
        ),
      );
      expect(screen.getByLabelText(/Sequence logo \(mix/)).toBeChecked();
      expect(screen.getByLabelText("Sort rows by")).toHaveValue("length");
      expect(screen.getByLabelText("MSA residue palette")).toHaveValue(
        residuePalette,
      );
      expect(screen.getByText("Descending sort")).toBeInTheDocument();
      expect(
        screen.getByText("What does Sequence logo show?").closest("details"),
      ).toHaveAttribute("open");
    },
  );

  it("keeps the matrix primary and makes one mounted tool panel visible at a time", async () => {
    const user = userEvent.setup();
    renderPreview([">alpha", "ACGT", ">beta", "ACGA"].join("\n"));
    await waitForViewer();

    const colorMode = screen.getByLabelText("MSA color mode");
    const rowVisibility = screen.getByLabelText("Show row alpha");
    expect(screen.getByLabelText("Search MSA motif")).toBeVisible();
    expect(screen.getByLabelText("Filter MSA rows")).toBeVisible();
    expect(colorMode).not.toBeVisible();
    expect(rowVisibility).not.toBeVisible();
    expect(colorMode.closest("[inert]")).not.toBeNull();

    await user.click(screen.getByRole("button", { name: "Display" }));
    expect(colorMode).toBeVisible();
    await user.click(screen.getByRole("button", { name: "Rows" }));
    expect(colorMode).not.toBeVisible();
    expect(rowVisibility).toBeVisible();
    expect(
      screen.getByLabelText("MSA row visibility controls"),
    ).not.toHaveClass("hidden");

    await user.click(screen.getByRole("button", { name: "Display" }));
    expect(screen.getByLabelText("MSA color mode")).toBe(colorMode);
    expect(rowVisibility).not.toBeVisible();
    await user.click(screen.getByRole("button", { name: "Display" }));
    expect(colorMode).not.toBeVisible();
    expect(
      screen.getByLabelText("Scrollable multiple sequence alignment matrix"),
    ).toBeVisible();
  });

  it("preserves the same search, filter, and edit inputs through tool switching and toolbar reveal", async () => {
    const user = userEvent.setup();
    const contents = [">alpha", "ACGT", ">beta", "ACGA"].join("\n");
    const view = renderWithMsaProviders(<MsaPreview contents={contents} />);
    await waitForViewer();
    const search = screen.getByLabelText("Search MSA motif");
    const filter = screen.getByLabelText("Filter MSA rows");
    await user.type(filter, "alpha");
    await user.type(search, "AC");
    await user.click(screen.getByRole("button", { name: "Edit copy" }));
    const groupName = screen.getByLabelText("Alignment row group name");
    await user.type(groupName, "Kinases");
    await user.click(screen.getByRole("button", { name: "Display" }));

    view.rerender(<MsaPreview contents={contents} toolbarVisible={false} />);
    expect(screen.getByLabelText("Search MSA motif")).toBe(search);
    expect(screen.getByLabelText("Filter MSA rows")).toBe(filter);
    expect(search).toHaveValue("AC");
    expect(filter).toHaveValue("alpha");
    expect(search).not.toBeVisible();
    expect(search.closest("[inert]")).not.toBeNull();
    expect(
      screen.getByLabelText("Scrollable multiple sequence alignment matrix"),
    ).toBeVisible();

    view.rerender(
      <MsaPreview contents={contents} toolbarRevealed toolbarVisible={false} />,
    );
    expect(search).toBeVisible();
    expect(filter).toBeVisible();
    expect(screen.getByLabelText("MSA color mode")).toBeVisible();
    await user.click(screen.getByRole("button", { name: "Edit copy" }));
    expect(screen.getByLabelText("Alignment row group name")).toBe(groupName);
    expect(groupName).toHaveValue("Kinases");
  });

  it("uses row-manager selection for reversible alignment edits", async () => {
    const user = userEvent.setup();
    renderPreview([">alpha", "ACGT", ">beta", "ACGA"].join("\n"));
    await waitForViewer();

    await user.click(screen.getByRole("button", { name: "Rows" }));
    await user.click(screen.getByLabelText("Select row alpha"));
    await user.click(screen.getByRole("button", { name: "Edit copy" }));
    await user.click(
      screen.getByRole("button", { name: "Remove selected rows" }),
    );
    expect(screen.queryByLabelText(/alpha column 1 A/)).not.toBeInTheDocument();
    expect(screen.getByLabelText(/beta column 1 A/)).toBeVisible();
    await user.click(screen.getByRole("button", { name: "Undo" }));
    expect(screen.getByLabelText(/alpha column 1 A/)).toBeVisible();
  });

  it.each(["button", "agent"] as const)(
    "keeps manual-copy feedback visible until the %s explicitly dismisses it",
    async (source) => {
      const user = userEvent.setup();
      const controller = new WorkbenchToolController();
      const writeText = vi
        .spyOn(navigator.clipboard, "writeText")
        .mockRejectedValue(new Error("Clipboard access denied"));
      try {
        renderWithMsaProviders(
          <WorkbenchToolControllerContext.Provider value={controller}>
            <MsaPreview
              contents={[">alpha", "ACGT", ">beta", "ACGA"].join("\n")}
            />
          </WorkbenchToolControllerContext.Provider>,
        );
        await waitForViewer();
        await user.click(screen.getByRole("button", { name: "Export" }));
        await user.click(
          screen.getByRole("button", { name: "Copy visible rows FASTA" }),
        );
        const manualCopy = await screen.findByRole("textbox", {
          name: "Alignment text to copy manually",
        });
        expect(manualCopy).toHaveValue(">alpha\nACGT\n>beta\nACGA");
        expect(controller.getFeedbackSnapshots("alignment")).toContainEqual(
          expect.objectContaining({
            id: "alignment.copy-feedback",
            kind: "copy",
            visible: true,
          }),
        );

        await user.click(screen.getByRole("button", { name: "Rows" }));
        expect(manualCopy).toBeVisible();
        expect(
          screen.getByText(
            "Finish or dismiss the action in the open tool before switching tools.",
          ),
        ).toBeVisible();
        if (source === "button") {
          await user.click(
            screen.getByRole("button", { name: "Dismiss copy feedback" }),
          );
        } else {
          act(() => {
            expect(
              controller.dismissFeedback("alignment.copy-feedback"),
            ).toMatchObject({ applied: true });
          });
        }
        await user.click(screen.getByRole("button", { name: "Rows" }));
        expect(screen.getByLabelText("Show row alpha")).toBeVisible();
        expect(
          screen.queryByLabelText("Alignment text to copy manually"),
        ).not.toBeInTheDocument();
      } finally {
        writeText.mockRestore();
      }
    },
  );

  it("opens registered alignment disclosures through the same mounted inspector controls", async () => {
    const controller = new WorkbenchToolController();
    renderWithMsaProviders(
      <WorkbenchToolControllerContext.Provider value={controller}>
        <MsaPreview
          contents={[
            "# STOCKHOLM 1.0",
            "#=GF ID alignment-family",
            "alpha ACGU",
            "beta A-GU",
            "//",
          ].join("\n")}
          filePath="/tmp/alignment.sto"
        />
      </WorkbenchToolControllerContext.Provider>,
    );
    await waitForViewer();
    act(() => {
      expect(
        controller.setDisclosure("alignment.metadata", true),
      ).toMatchObject({ applied: true });
    });
    expect(screen.getByRole("button", { name: "Details" })).toHaveAttribute(
      "aria-expanded",
      "true",
    );
    const metadata = screen.getByText("Parsed alignment metadata");
    expect(metadata).toBeVisible();
    expect(metadata.closest("details")).toHaveAttribute("open");
    act(() => {
      expect(
        controller.setDisclosure("alignment.guide-tree", true),
      ).toMatchObject({ applied: true });
    });
    expect(screen.getByRole("button", { name: "Analyze" })).toHaveAttribute(
      "aria-expanded",
      "true",
    );
    expect(
      screen.getByRole("button", { name: "Compute guide tree" }),
    ).toBeVisible();
    expect(metadata).not.toBeVisible();
  });

  it("skips alignment hashing for an authenticated source key and preserves the fallback", async () => {
    const createArtifactStateKey = vi.spyOn(
      artifactStateKey,
      "createArtifactStateKey",
    );
    const contents = [">a", "ACGT", ">b", "ACGA"].join("\n");

    try {
      const view = renderWithMsaProviders(
        <MsaPreview
          contents={contents}
          filePath="/tmp/trusted.afa"
          sourceStateKeyOverride="authenticated-alignment-revision"
        />,
      );
      await waitForViewer();

      expect(createArtifactStateKey).not.toHaveBeenCalled();

      view.rerender(
        <IntlProvider locale="en">
          <MsaPreview contents={contents} filePath="/tmp/trusted.afa" />
        </IntlProvider>,
      );

      expect(createArtifactStateKey).toHaveBeenCalledWith(
        contents,
        "/tmp/trusted.afa",
      );
    } finally {
      createArtifactStateKey.mockRestore();
    }
  });

  it("shows an honest parsing shell before the viewer becomes usable", async () => {
    renderPreview([">a", "AA", ">b", "AG"].join("\n"));

    expect(screen.getByText("Parsing alignment…")).toBeInTheDocument();
    expect(await waitForViewer()).toBeInTheDocument();
  });

  it("renders a biology-aware rich DNA viewer and search navigation", async () => {
    const user = userEvent.setup();
    renderPreview([">a", "AAGT", ">b", "ACGT"].join("\n"));

    expect(await waitForViewer()).toBeInTheDocument();
    expect(screen.getAllByText("DNA").length).toBeGreaterThan(0);
    expect(screen.getByText("2 sequences")).toBeInTheDocument();
    expect(screen.getByLabelText("Annotations unavailable")).toBeDisabled();

    await user.type(screen.getByLabelText("Search MSA motif"), "ACT");
    expect(
      await screen.findByText(/Motif search results · 1 hits/),
    ).toBeInTheDocument();
    expect(screen.getByText(/ungapped 2-4/)).toBeInTheDocument();
    expect(
      (await screen.findAllByText(/reverse-complement/)).length,
    ).toBeGreaterThan(0);
    await user.click(screen.getByText("Next"));
    expect(
      await screen.findByText(/Match a:2-4 \(reverse-complement\)/),
    ).toBeInTheDocument();
  });

  it("shows analytical tracks, coordinate jump controls, export actions, and a pinned inspector", async () => {
    renderPreview([">a", "AAGT", ">b", "ACGT"].join("\n"));

    await waitForViewer();
    await userEvent.click(screen.getByRole("button", { name: "Display" }));
    expect(screen.getByText("Tracks")).toBeInTheDocument();
    expect(screen.getAllByText("Identity histogram").length).toBeGreaterThan(0);
    expect(
      screen.getByText("Sequence logo (mix + information)"),
    ).toBeInTheDocument();
    expect(screen.getByText("optional")).toBeInTheDocument();
    expect(
      screen.queryByText("What does Sequence logo show?"),
    ).not.toBeInTheDocument();
    const logoLabel = screen
      .getByText("Sequence logo (mix + information)")
      .closest("label");
    if (logoLabel == null) {
      throw new Error("Expected the sequence-logo toggle label to render.");
    }
    const logoToggle = logoLabel.querySelector("input");
    if (!(logoToggle instanceof HTMLInputElement)) {
      throw new Error("Expected the sequence-logo toggle input to render.");
    }
    fireEvent.click(logoToggle);
    expect(
      screen.getByText("What does Sequence logo show?"),
    ).toBeInTheDocument();
    fireEvent.click(screen.getByText("What does Sequence logo show?"));
    expect(
      screen.getByText(
        "Tall single-letter stacks indicate a highly informative column; mixed letters indicate compositional variation.",
      ),
    ).toBeInTheDocument();
    expect(await screen.findByText("Alignment cols")).toBeInTheDocument();
    expect(
      screen.getByLabelText("Jump to MSA alignment column"),
    ).toBeInTheDocument();
    expect(screen.getByText("Copy / export")).toBeInTheDocument();

    fireEvent.click(screen.getByLabelText(/a column 1 A/));
    expect(screen.getByText("Pinned cell inspector")).toBeInTheDocument();
    expect(screen.getByText("Alignment column")).toBeInTheDocument();
  });

  it("rejects out-of-range alignment coordinates and ambiguous row aliases", async () => {
    const onCommandResult = vi.fn();
    const contents = [">dup", "ACGT", ">dup", "ACGA"].join("\n");
    const view = renderWithMsaProviders(
      <MsaPreview
        command={alignmentCommand({
          action: "focus_alignment_cell",
          column: 99,
          row: "dup__1",
        })}
        contents={contents}
        onCommandResult={onCommandResult}
      />,
    );
    await waitForViewer();
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: false,
          message: expect.stringContaining("outside"),
          state: { maxColumn: 4, minColumn: 1 },
        }),
      );
    });

    view.rerender(
      <IntlProvider locale="en">
        <MsaPreview
          command={alignmentCommand(
            { action: "focus_alignment_cell", column: 2, row: "dup" },
            2,
          )}
          contents={contents}
          onCommandResult={onCommandResult}
        />
      </IntlProvider>,
    );
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.anything(),
        expect.objectContaining({
          applied: false,
          message: expect.stringContaining(
            "More than one visible alignment row",
          ),
          state: { candidateRowIds: ["dup__1", "dup__2"] },
        }),
      );
    });
  });

  it("pushes text plus structured model context for the active alignment view and range selection", async () => {
    const updateModelContext = vi.fn();
    renderWithMsaProviders(
      <MsaPreview
        className="h-full"
        contents={[">a", "AAGT", ">b", "ACGT"].join("\n")}
        filePath="/tmp/alignment.afa"
        updateModelContext={updateModelContext}
      />,
    );

    await waitForViewer();
    await waitFor(() => {
      expect(updateModelContext).toHaveBeenLastCalledWith(
        expect.objectContaining({
          structuredContent: expect.objectContaining({
            rowCoordinateMaps: expect.arrayContaining([
              expect.objectContaining({
                coordinateIntervals: expect.arrayContaining([
                  expect.objectContaining({
                    alignmentEndColumn: 4,
                    alignmentStartColumn: 1,
                    ungappedEndPosition: 4,
                    ungappedStartPosition: 1,
                  }),
                ]),
                rowLabel: "a",
              }),
            ]),
            viewer: "alignment",
          }),
          text: expect.stringContaining(
            "Current scientific viewer: Alignment viewer",
          ),
        }),
      );
    });

    fireEvent.pointerDown(
      screen.getByLabelText(
        "Start or extend an MSA column selection at column 1",
      ),
    );
    fireEvent.pointerEnter(
      screen.getByLabelText(
        "Start or extend an MSA column selection at column 2",
      ),
    );

    await waitFor(() => {
      expect(updateModelContext).toHaveBeenLastCalledWith(
        expect.objectContaining({
          structuredContent: expect.objectContaining({
            selection: expect.objectContaining({
              columns: [
                expect.objectContaining({
                  alignmentColumn: 1,
                  rows: [
                    expect.objectContaining({
                      rowLabel: "a",
                      symbol: "A",
                      ungappedPosition: 1,
                    }),
                    expect.objectContaining({
                      rowLabel: "b",
                      symbol: "A",
                      ungappedPosition: 1,
                    }),
                  ],
                }),
                expect.objectContaining({
                  alignmentColumn: 2,
                  conservation: expect.objectContaining({
                    identity: 0.5,
                  }),
                  rows: [
                    expect.objectContaining({
                      rowLabel: "a",
                      symbol: "A",
                      ungappedPosition: 2,
                    }),
                    expect.objectContaining({
                      rowLabel: "b",
                      symbol: "C",
                      ungappedPosition: 2,
                    }),
                  ],
                }),
              ],
              end: 2,
              start: 1,
            }),
          }),
          text: expect.stringContaining(
            "Selected column details: col 1 consensus A",
          ),
        }),
      );
    });
  });

  it("applies every alignment command exposed to chat", async () => {
    const contents = [">ref", "AAGT", ">mut", "ACGT"].join("\n");
    const onCommandResult = vi.fn();
    const view = renderWithMsaProviders(
      <MsaPreview
        command={alignmentCommand({
          action: "select_alignment_columns",
          end: 3,
          start: 2,
        })}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );

    await waitForViewer();
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "select_alignment_columns" }),
        expect.objectContaining({ applied: true, state: { end: 3, start: 2 } }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "focus_alignment_cell", column: 2, row: "mut" },
          2,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(screen.getByText("Pinned cell inspector")).toBeInTheDocument();
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "focus_alignment_cell" }),
        expect.objectContaining({
          applied: true,
          state: { column: 2, rowId: "mut", symbol: "C" },
        }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "search_alignment", query: "CG" },
          3,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(screen.getByLabelText("Search MSA motif")).toHaveValue("CG");
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "search_alignment" }),
        expect.objectContaining({ applied: true, state: { query: "CG" } }),
      );
      expect(
        screen.getByText(/Motif search results · 1 hits/),
      ).toBeInTheDocument();
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "navigate_alignment_search_hit", direction: "next" },
          4,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "navigate_alignment_search_hit" }),
        expect.objectContaining({
          applied: true,
          state: expect.objectContaining({ hitCount: 1, hitIndex: 1 }),
        }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "set_alignment_reference", reference: "mut" },
          5,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(screen.getByLabelText("MSA reference mode")).toHaveValue("anchor");
      expect(screen.getByLabelText("MSA anchor row")).toHaveValue("mut");
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "set_alignment_reference" }),
        expect.objectContaining({
          applied: true,
          state: { referenceMode: "anchor", rowId: "mut" },
        }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "focus_alignment_reference_coordinate", coordinate: 2 },
          6,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({
          action: "focus_alignment_reference_coordinate",
        }),
        expect.objectContaining({
          applied: true,
          state: expect.objectContaining({
            alignmentColumn: 2,
            coordinate: 2,
            referenceRowId: "mut",
          }),
        }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "filter_alignment_rows", query: "ref" },
          7,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(screen.getByLabelText("Filter MSA rows")).toHaveValue("ref");
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "filter_alignment_rows" }),
        expect.objectContaining({
          applied: true,
          state: { matchingRowCount: 1, query: "ref" },
        }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          {
            action: "set_alignment_row_visibility",
            rows: ["mut"],
            visible: false,
          },
          8,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "set_alignment_row_visibility" }),
        expect.objectContaining({
          applied: true,
          state: expect.objectContaining({ visibleRowCount: 1 }),
        }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand({ action: "show_all_alignment_rows" }, 9)}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "show_all_alignment_rows" }),
        expect.objectContaining({
          applied: true,
          state: { visibleRowCount: 2 },
        }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          {
            action: "set_alignment_view_options",
            analysisScope: "currently-displayed-rows",
            cellWidth: 24,
            colorMode: "nucleotide-substitution",
            searchScope: "all-unhidden-rows",
            showIdenticalAsDots: true,
          },
          10,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(screen.getByLabelText("MSA analysis scope")).toHaveValue(
        "currently-displayed-rows",
      );
      expect(screen.getByLabelText("MSA motif search scope")).toHaveValue(
        "all-unhidden-rows",
      );
      expect(screen.getByLabelText("MSA color mode")).toHaveValue(
        "nucleotide-substitution",
      );
      expect(screen.getByLabelText("Dots for matches")).toBeChecked();
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "set_alignment_view_options" }),
        expect.objectContaining({ applied: true }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand({ action: "clear_alignment_selection" }, 11)}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "clear_alignment_selection" }),
        expect.objectContaining({ applied: true }),
      );
    });

    view.rerender(
      <MsaPreview
        command={alignmentCommand(
          { action: "compute_alignment_guide_tree" },
          12,
        )}
        contents={contents}
        filePath="/tmp/commands.afa"
        onCommandResult={onCommandResult}
      />,
    );
    await waitFor(() => {
      expect(onCommandResult).toHaveBeenLastCalledWith(
        expect.objectContaining({ action: "compute_alignment_guide_tree" }),
        expect.objectContaining({
          applied: true,
          state: expect.objectContaining({ method: "UPGMA" }),
        }),
      );
    });
    fireEvent.click(screen.getByRole("button", { name: "Analyze" }));
    const guideTreeSummary = screen.getByText("Exploratory guide tree");
    fireEvent.click(guideTreeSummary);
    const guideTreeDetails = guideTreeSummary.closest("details");
    if (guideTreeDetails == null)
      throw new Error("Expected guide-tree details.");
    expect(within(guideTreeDetails).getByText(/'ref'/)).toBeVisible();
  });

  it("includes enough selected-column context to answer residue mapping and conservation follow-ups", async () => {
    const updateModelContext = vi.fn();
    renderWithMsaProviders(
      <MsaPreview
        className="h-full"
        contents={[
          ">KRAS_G12C_HUMAN",
          "LLLLLLLLLLL-------------C",
          ">KRAS_WT_HUMAN",
          "LLLLLLLLLLL-------------G",
        ].join("\n")}
        filePath="/tmp/kras-g12c.afa"
        updateModelContext={updateModelContext}
      />,
    );

    await waitForViewer();
    fireEvent.pointerDown(
      screen.getByLabelText(
        "Start or extend an MSA column selection at column 25",
      ),
    );

    await waitFor(() => {
      expect(updateModelContext).toHaveBeenLastCalledWith(
        expect.objectContaining({
          structuredContent: expect.objectContaining({
            rowCoordinateMaps: expect.arrayContaining([
              expect.objectContaining({
                coordinateIntervals: expect.arrayContaining([
                  expect.objectContaining({
                    alignmentEndColumn: 25,
                    alignmentStartColumn: 25,
                    ungappedEndPosition: 12,
                    ungappedStartPosition: 12,
                  }),
                ]),
                rowLabel: "KRAS_G12C_HUMAN",
              }),
            ]),
            selection: expect.objectContaining({
              columns: [
                expect.objectContaining({
                  alignmentColumn: 25,
                  conservation: expect.objectContaining({
                    identity: 0.5,
                  }),
                  rows: [
                    expect.objectContaining({
                      rowLabel: "KRAS_G12C_HUMAN",
                      symbol: "C",
                      ungappedPosition: 12,
                    }),
                    expect.objectContaining({
                      rowLabel: "KRAS_WT_HUMAN",
                      symbol: "G",
                      ungappedPosition: 12,
                    }),
                  ],
                }),
              ],
            }),
          }),
          text: expect.stringContaining(
            "Selected column details: col 25 consensus",
          ),
        }),
      );
    });
  });

  it("keeps tracks windowed to the displayed alignment columns when paging horizontally", async () => {
    renderPreview(generateAlignment(2, 120));

    await waitForViewer();
    expect(
      screen.getByText(
        "Tracks show the currently displayed alignment columns 1-84.",
      ),
    ).toBeInTheDocument();

    fireEvent.click(screen.getByText("Next columns"));

    expect(
      screen.getByText(
        "Tracks show the currently displayed alignment columns 25-108.",
      ),
    ).toBeInTheDocument();
  });

  it("keeps horizontal scrolling scoped to the alignment matrix viewport", async () => {
    renderPreview(generateAlignment(2, 500));

    await waitForViewer();
    const matrixViewport = screen.getByLabelText(
      "Scrollable multiple sequence alignment matrix",
    );
    expect(matrixViewport).toHaveClass("overflow-auto");
    expect(matrixViewport.parentElement).toHaveClass(
      "min-w-0",
      "overflow-hidden",
    );
  });

  it("scrolls the shared alignment viewport from tracks, overview, and slider gestures", async () => {
    renderPreview(generateAlignment(2, 500));

    await waitForViewer();
    const firstWindow =
      "Tracks show the currently displayed alignment columns 1-84.";
    const secondWindow =
      "Tracks show the currently displayed alignment columns 25-108.";
    const slider = screen.getByLabelText("Move MSA overview window");

    expect(screen.getByText(firstWindow)).toBeInTheDocument();
    fireEvent.wheel(screen.getByLabelText("MSA metric tracks"), {
      deltaX: 600,
    });
    expect(screen.getByText(secondWindow)).toBeInTheDocument();

    fireEvent.change(slider, { target: { value: "0" } });
    expect(screen.getByText(firstWindow)).toBeInTheDocument();
    fireEvent.wheel(screen.getByLabelText("MSA overview strip"), {
      deltaX: 600,
    });
    expect(screen.getByText(secondWindow)).toBeInTheDocument();

    fireEvent.change(slider, { target: { value: "0" } });
    expect(screen.getByText(firstWindow)).toBeInTheDocument();
    fireEvent.wheel(slider, { deltaX: 600 });
    expect(screen.getByText(secondWindow)).toBeInTheDocument();
  });

  it("supports drag-style column range selection from the ruler and selected-range exports", async () => {
    renderPreview([">a", "AAGT", ">b", "ACGT"].join("\n"));

    await waitForViewer();
    fireEvent.pointerDown(
      screen.getByLabelText(
        "Start or extend an MSA column selection at column 1",
      ),
    );
    fireEvent.pointerEnter(
      screen.getByLabelText(
        "Start or extend an MSA column selection at column 2",
      ),
    );
    expect(screen.getByText("Selected columns 1-2")).toBeInTheDocument();
    expect(screen.getByText("2 columns")).toBeInTheDocument();
    expect(screen.getByText("Copy selected range")).toBeInTheDocument();
    expect(
      screen.getByText("Workspace export is unavailable for this viewer."),
    ).toBeInTheDocument();
    expect(screen.queryByText("Download selected SVG")).not.toBeInTheDocument();
    fireEvent.click(screen.getByText("Clear selection"));
    expect(screen.queryByText("Selected columns 1-2")).toBeNull();
  });

  it("shows RNA structural annotations, row filters, interpretation override, and focus details", async () => {
    renderPreview(
      [
        "# STOCKHOLM 1.0",
        "rna1 AC-G",
        "rna2 AU-G",
        "#=GC SS_cons <<>>",
        "#=GC RF xxxx",
        "//",
      ].join("\n"),
      "/tmp/rfam.sto",
    );

    await waitForViewer();
    expect(screen.getAllByText("RNA").length).toBeGreaterThan(0);
    expect(screen.getByText("SS_cons")).toBeInTheDocument();
    fireEvent.click(screen.getByLabelText("Annotations"));
    expect(screen.queryByText("SS_cons")).toBeNull();
    fireEvent.click(screen.getByLabelText("Annotations"));
    expect(screen.getByText("SS_cons")).toBeInTheDocument();
    fireEvent.change(screen.getByLabelText("Filter MSA rows"), {
      target: { value: "rna1" },
    });
    expect(screen.getAllByText("rna1").length).toBeGreaterThan(0);
    fireEvent.change(screen.getByLabelText("MSA molecule interpretation"), {
      target: { value: "protein" },
    });
    expect(screen.getAllByText("Protein").length).toBeGreaterThan(0);
    fireEvent.mouseEnter(screen.getByLabelText(/rna1 column 1 A/));
    expect(screen.queryByText(/rna1 · col 1 · A/)).toBeNull();
    expect(screen.getByRole("tooltip")).toHaveTextContent("rna1");
    expect(screen.getByRole("tooltip")).toHaveTextContent("Column 1");
    expect(screen.getByRole("tooltip")).toHaveTextContent(
      "Ungapped position 1",
    );
    fireEvent.focus(screen.getByLabelText(/rna1 column 1 A/));
    expect(screen.getByText(/rna1 · col 1 · A/)).toBeInTheDocument();
    fireEvent.mouseLeave(screen.getByLabelText(/rna1 column 1 A/));
    expect(screen.queryByRole("tooltip")).toBeNull();
    fireEvent.mouseEnter(screen.getByLabelText(/rna1 column 3 -/));
    expect(screen.getByRole("tooltip")).toHaveTextContent("Symbol -");
    expect(screen.getByRole("tooltip")).not.toHaveTextContent("Ungapped");
  });

  it("renders a parse error panel instead of a misleading matrix", async () => {
    renderPreview("plain text", "/tmp/not-alignment.txt");
    expect(
      await screen.findByText(
        "Codex could not determine a supported MSA text format.",
      ),
    ).toBeInTheDocument();
  });

  it("uses a hidden A3M format hint path for workspace previews", async () => {
    renderWithMsaProviders(
      <MsaPreview
        className="h-full"
        contents={[">query", "AC-DE", ">hit", "ACaa-DE"].join("\n")}
        formatHintPath="/tmp/protein-profile.a3m"
      />,
    );

    await waitForViewer();
    expect(screen.queryByText(/common display width/i)).toBeNull();
    expect(screen.getAllByText("Protein").length).toBeGreaterThan(0);
    expect(screen.getByLabelText("MSA color mode")).toHaveValue("residue");
    expect(screen.getByLabelText("MSA residue palette")).toHaveValue(
      "muted-amino-acid",
    );
    expect(screen.getAllByText("Soft amino acid").length).toBeGreaterThan(0);
    expect(screen.getByText("+2")).toBeInTheDocument();
    expect(
      screen.getByText(
        "A2M/A3M insertion after this aligned column (+n = hidden inserted residues)",
      ),
    ).toBeInTheDocument();
    expect(screen.queryByText("protein-profile.a3m")).toBeNull();
  });

  it("defaults nucleic acids to Differences and exposes residue palettes only in residue mode", async () => {
    renderPreview([">a", "AAGT", ">b", "ACGT"].join("\n"));

    await waitForViewer();
    expect(screen.getByLabelText("MSA color mode")).toHaveValue("difference");
    expect(screen.queryByLabelText("MSA residue palette")).toBeNull();
    expect(await screen.findByText("Match / baseline")).toBeInTheDocument();
    expect(
      await screen.findByText("Difference from reference"),
    ).toBeInTheDocument();

    fireEvent.change(screen.getByLabelText("MSA color mode"), {
      target: { value: "residue" },
    });

    const paletteSelect = screen.getByLabelText("MSA residue palette");
    expect(paletteSelect).toHaveValue("muted-nucleic-acid");
    expect(screen.getAllByText("Soft nucleotide").length).toBeGreaterThan(0);
    expect(screen.queryByText("RasMol")).toBeNull();
  });

  it.each([
    {
      molecule: "dna",
      palette: "muted-nucleic-acid",
      sequence: "AGTN-",
      path: "/tmp/soft-dna.afa",
      column: 2,
      symbol: "G",
      legend: "G",
    },
    {
      molecule: "rna",
      palette: "muted-nucleic-acid",
      sequence: "AGUN-",
      path: "/tmp/soft-rna.afa",
      column: 3,
      symbol: "U",
      legend: "T/U",
    },
    {
      molecule: "protein",
      palette: "muted-amino-acid",
      sequence: "KRUB-",
      path: "/tmp/soft-protein.a3m",
      column: 1,
      symbol: "K",
      legend: "Positive",
    },
  ] as const)(
    "controls and discovers the soft $molecule palette with matching legend pairs",
    async ({ molecule, palette, sequence, path, column, symbol, legend }) => {
      const onCommandResult = vi.fn();
      const updateModelContext = vi.fn();
      const contents = `>row\n${sequence}\n>other\n${sequence}`;
      renderWithMsaProviders(
        <MsaPreview
          command={alignmentCommand({
            action: "set_alignment_view_options",
            colorMode: "residue",
            residuePalette: palette,
          })}
          contents={contents}
          filePath={path}
          onCommandResult={onCommandResult}
          updateModelContext={updateModelContext}
        />,
      );
      await waitForViewer();
      await waitFor(() =>
        expect(onCommandResult).toHaveBeenLastCalledWith(
          expect.anything(),
          expect.objectContaining({
            applied: true,
            state: expect.objectContaining({ residuePalette: palette }),
          }),
        ),
      );
      const cell = screen.getByLabelText(`row column ${column} ${symbol}`);
      const expected = getSequenceResidueStyle({
        molecule,
        paletteId: palette,
        residue: symbol,
      });
      expect(cell.style.backgroundColor).toBe(expected.backgroundColor);
      expect(cell.style.color).toBe(expected.color);
      const swatch = within(
        screen.getByLabelText("MSA color legend"),
      ).getByText(legend);
      expect(swatch.style.backgroundColor).toBe(expected.backgroundColor);
      expect(swatch.style.color).toBe(expected.color);
      await waitFor(() =>
        expect(updateModelContext).toHaveBeenLastCalledWith(
          expect.objectContaining({
            structuredContent: expect.objectContaining({
              display: expect.objectContaining({
                residuePalette: palette,
                defaultResiduePalette: palette,
                compatibleResiduePalettes: expect.arrayContaining([
                  palette,
                  "neutral",
                ]),
              }),
            }),
          }),
        ),
      );
      fireEvent.change(screen.getByLabelText("MSA residue palette"), {
        target: { value: "neutral" },
      });
      expect(cell.style.backgroundColor).toBe("transparent");
      expect(cell.style.color).toBe("var(--bio-token-text-primary)");
    },
  );

  it("resets incompatible palettes when interpretation crosses modality families", async () => {
    renderPreview([">a", "KR", ">b", "KR"].join("\n"), "/tmp/family.a3m");

    await waitForViewer();
    expect(screen.getByLabelText("MSA residue palette")).toHaveValue(
      "muted-amino-acid",
    );
    fireEvent.change(screen.getByLabelText("MSA molecule interpretation"), {
      target: { value: "dna" },
    });

    expect(screen.getByLabelText("MSA residue palette")).toHaveValue(
      "muted-nucleic-acid",
    );
    expect(screen.getAllByText("Soft nucleotide").length).toBeGreaterThan(0);
  });

  it("updates the adaptive legend for major palette and mode changes", async () => {
    renderPreview([">a", "KR", ">b", "KR"].join("\n"), "/tmp/family.a3m");

    await waitForViewer();
    fireEvent.change(screen.getByLabelText("MSA residue palette"), {
      target: { value: "rasmol" },
    });
    expect(screen.getAllByText("RasMol").length).toBeGreaterThan(0);
    fireEvent.change(screen.getByLabelText("MSA residue palette"), {
      target: { value: "hydrophobicity" },
    });
    expect(screen.getAllByText("Hydrophobicity").length).toBeGreaterThan(0);
    expect(screen.getByText("hydrophobic")).toBeInTheDocument();
    expect(screen.getByText("hydrophilic")).toBeInTheDocument();

    fireEvent.change(screen.getByLabelText("MSA color mode"), {
      target: { value: "protein-conservation" },
    });
    expect(
      (await screen.findAllByText("Protein conservation")).length,
    ).toBeGreaterThan(0);

    fireEvent.change(screen.getByLabelText("MSA color mode"), {
      target: { value: "difference" },
    });
    expect(screen.getByText("Match / baseline")).toBeInTheDocument();
    fireEvent.change(screen.getByLabelText("MSA reference mode"), {
      target: { value: "none" },
    });
    expect(
      screen.getByText(
        "Select a consensus or anchor reference to highlight differences.",
      ),
    ).toBeInTheDocument();
  });

  it("supports nucleic-acid palette legends and ambiguity-aware RNA residue colors", async () => {
    renderPreview(
      [">rna1", "AUR", ">rna2", "AUR"].join("\n"),
      "/tmp/rna.aln-fasta",
    );

    await waitForViewer();
    fireEvent.change(screen.getByLabelText("MSA color mode"), {
      target: { value: "residue" },
    });
    fireEvent.change(screen.getByLabelText("MSA residue palette"), {
      target: { value: "purine-pyrimidine" },
    });
    expect(screen.getAllByText("Purine / Pyrimidine").length).toBeGreaterThan(
      0,
    );
    expect(screen.getByText("Purines A/G/R")).toBeInTheDocument();

    fireEvent.change(screen.getByLabelText("MSA residue palette"), {
      target: { value: "nucleotide-ambiguity" },
    });
    expect(screen.getAllByText("Nucleotide ambiguity").length).toBeGreaterThan(
      0,
    );
    expect(screen.getByText("R purine")).toBeInTheDocument();
    expect(screen.getByLabelText(/rna1 column 2 U/)).toBeInTheDocument();
  });

  it("renders coding-DNA translation helpers and exposes coding-aware color modes", async () => {
    renderPreview(
      [">ref", "ATGAAA", ">syn", "ATGAAG", ">nonsyn", "ATGGAA"].join("\n"),
      "/tmp/coding.afa",
    );

    await waitForViewer();
    expect(screen.getByText("AA translation")).toBeInTheDocument();
    const translation = screen.getByText("AA translation").parentElement!;
    const methionine = within(translation).getByText("M");
    expect(methionine.style.backgroundColor).toBe(
      "var(--bio-protein-hydrophobic-surface)",
    );
    expect(methionine.style.color).toBe(
      "var(--bio-protein-hydrophobic-foreground)",
    );
    expect(translation).toHaveAttribute(
      "title",
      expect.stringContaining("not substitution impact or conservation"),
    );
    expect(screen.getByLabelText("MSA color mode")).toHaveTextContent(
      "Transition / transversion",
    );
    expect(screen.getByLabelText("MSA color mode")).toHaveTextContent(
      "Synonymous / nonsynonymous",
    );
  });

  it("keeps dark canonical nucleotide palette cells legible with a light foreground", async () => {
    renderPreview(
      [">dna_g", "AGT", ">dna_g2", "AGT"].join("\n"),
      "/tmp/readable-dna.aln-fasta",
    );

    await waitForViewer();
    fireEvent.change(screen.getByLabelText("MSA color mode"), {
      target: { value: "residue" },
    });
    fireEvent.change(screen.getByLabelText("MSA residue palette"), {
      target: { value: "ncbi-nucleic-acid" },
    });

    expect(screen.getByLabelText(/dna_g column 2 G/)).toHaveStyle({
      backgroundColor: "#0000ff",
      color: "#ffffff",
    });
  });

  it("renders Differences mode as neutral matches plus highlighted mismatches", async () => {
    renderPreview([">ref", "AA", ">mut", "AG"].join("\n"));

    await waitForViewer();
    fireEvent.change(screen.getByLabelText("MSA reference mode"), {
      target: { value: "anchor" },
    });
    expect(screen.getByLabelText("MSA anchor row")).toHaveValue("ref");

    expect(screen.getByLabelText(/ref column 2 A/)).toHaveStyle({
      backgroundColor: "#f8fafc",
    });
    expect(screen.getByLabelText(/mut column 2 G/)).toHaveStyle({
      backgroundColor: "#ffd6a8",
    });
  });

  it("keeps mounted matrix cells and row controls bounded for large alignments", async () => {
    renderPreview(generateAlignment(400, 500), "/tmp/large.afa");

    const viewer = await waitForViewer();
    const matrixCells = viewer.querySelectorAll(
      'button[aria-label*=" column "]',
    );
    expect(matrixCells.length).toBeGreaterThan(0);
    expect(matrixCells.length).toBeLessThan(6_000);

    const rowControls = screen.getByLabelText("MSA row visibility controls");
    const rowCheckboxes = rowControls.querySelectorAll(
      'input[type="checkbox"]',
    );
    expect(rowCheckboxes.length).toBeGreaterThan(0);
    expect(rowCheckboxes.length).toBeLessThan(60);
  }, 15_000);

  it("computes tooltip geometry lazily only after an active cell is hovered", async () => {
    const rectSpy = vi.spyOn(HTMLElement.prototype, "getBoundingClientRect");
    renderPreview([">a", "AA", ">b", "AG"].join("\n"));
    await waitForViewer();

    expect(rectSpy).not.toHaveBeenCalled();
    fireEvent.mouseEnter(screen.getByLabelText(/a column 1 A/));
    expect(rectSpy).toHaveBeenCalledTimes(1);
    expect(screen.getByRole("tooltip")).toHaveTextContent(
      "Ungapped position 1",
    );
    rectSpy.mockRestore();
  });

  it("exposes an accessible grid with one roving tab stop and arrow navigation", async () => {
    renderPreview([">a", "AAGT", ">b", "ACGT"].join("\n"));
    const grid = await waitForViewer();
    expect(grid).toHaveAttribute("role", "grid");
    const cells = screen.getAllByRole("gridcell", { name: /column/ });
    expect(cells.filter((cell) => cell.tabIndex === 0)).toHaveLength(1);
    screen.getByLabelText(/a column 1 A/).focus();
    await userEvent.keyboard("{ArrowRight}");
    expect(screen.getByLabelText(/a column 2 A/)).toHaveFocus();
  });

  it("announces optimized mode for especially large row counts", async () => {
    renderPreview(generateAlignment(1_001, 4), "/tmp/large-rows.afa");

    await waitForViewer();
    expect(
      screen.getByText("Large alignment optimized mode"),
    ).toBeInTheDocument();
  });
});

function alignmentCommand(
  command: SequenceViewerCommand,
  revision = 1,
): QueuedSequenceViewerCommand {
  return {
    ...command,
    commandId: `11111111-1111-4111-8111-${revision.toString().padStart(12, "0")}`,
    revision,
  } as QueuedSequenceViewerCommand;
}

SHA-256: 8f9121536894adb62008890cd31886115742866d6e95d51380131afd6608ca85