This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new d249f123106 UI: Split log copy and selection tests out of 
Logs.test.tsx (#71581)
d249f123106 is described below

commit d249f1231067b120fbc83f9f25f36d1a6eea247e
Author: Stefan Wang <[email protected]>
AuthorDate: Fri Aug 14 06:28:52 2026 -0700

    UI: Split log copy and selection tests out of Logs.test.tsx (#71581)
    
    Logs.test.tsx crossed the 500-line max-lines limit, so eslint now fails
    static checks on every PR whose merge ref is recomputed against main.
    The copy and selection cases are self-contained, so they move to their own
    file next to the other per-concern test files in that directory.
    
    Signed-off-by: 1fanwang <[email protected]>
---
 .../ui/src/pages/TaskInstance/Logs/Logs.test.tsx   | 389 +--------------------
 .../Logs/{Logs.test.tsx => LogsSelection.test.tsx} | 389 ---------------------
 2 files changed, 1 insertion(+), 777 deletions(-)

diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
index e0da0bb4bd5..bb3f7953741 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
@@ -18,7 +18,7 @@
  */
 import "@testing-library/jest-dom";
 import { fireEvent, render, screen, waitFor } from "@testing-library/react";
-import { beforeAll, describe, expect, it, vi } from "vitest";
+import { beforeAll, describe, expect, it } from "vitest";
 
 import { AppWrapper } from "src/utils/AppWrapper";
 
@@ -438,390 +438,3 @@ describe("Task log search", () => {
     await expectRenderedLineNumber(/starting attempt 1 of 3/iu, 3);
   }, 10_000);
 });
-
-const makeClipboardData = () => {
-  const store = new Map<string, string>();
-
-  return {
-    getData: (type: string) => store.get(type) ?? "",
-    setData: (type: string, value: string) => store.set(type, value),
-  };
-};
-
-const dispatchCopy = (clipboardData: ReturnType<typeof makeClipboardData>) => {
-  const copyEvent = new Event("copy", { bubbles: true, cancelable: true });
-
-  Object.defineProperty(copyEvent, "clipboardData", { value: clipboardData });
-  document.dispatchEvent(copyEvent);
-
-  return copyEvent;
-};
-
-const findRow = (text: string) => {
-  const container = screen.getByTestId("virtual-scroll-container");
-
-  return [...container.querySelectorAll("[data-index]")].find((row) =>
-    row.textContent.includes(text),
-  ) as HTMLElement;
-};
-
-const getRowCopyText = (row: HTMLElement) => {
-  const clone = row.cloneNode(true) as HTMLElement;
-
-  for (const element of clone.querySelectorAll("[data-copy-exclude]")) {
-    element.remove();
-  }
-
-  return clone.textContent;
-};
-
-const withFakeSelection = <T,>(selection: Selection, callback: () => T): T => {
-  const getSelectionSpy = vi.spyOn(document, 
"getSelection").mockReturnValue(selection);
-  const result = callback();
-
-  getSelectionSpy.mockRestore();
-
-  return result;
-};
-
-describe("Copy across virtualized rows", () => {
-  it("rebuilds a removed grouped row from log data and strips line numbers", 
async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-    await waitForLogs();
-
-    fireEvent.click(screen.getByTestId("summary-Pre Execute"));
-    await waitFor(() => expect(screen.getByText(/DAG bundles 
loaded/iu)).toBeInTheDocument());
-
-    const firstRow = findRow("Task started");
-    const middleRow = findRow("DAG bundles loaded");
-    const lastRow = findRow("Done. Returned value was: None");
-
-    expect(firstRow).toBeDefined();
-    expect(middleRow).toBeDefined();
-    expect(lastRow).toBeDefined();
-
-    middleRow.remove();
-
-    const range = document.createRange();
-
-    range.setStart(firstRow, 0);
-    range.setEnd(lastRow, lastRow.childNodes.length);
-
-    const selection = { getRangeAt: () => range, isCollapsed: false, 
rangeCount: 1 } as unknown as Selection;
-    const clipboardData = makeClipboardData();
-    const copyEvent = withFakeSelection(selection, () => 
dispatchCopy(clipboardData));
-
-    expect(copyEvent.defaultPrevented).toBe(true);
-
-    const lines = clipboardData.getData("text/plain").split("\n");
-
-    expect(lines[0]).not.toMatch(/^\d/u);
-    expect(lines[0]).toContain("Task started");
-    expect(lines).toContainEqual(
-      expect.stringMatching(/^\[.+\] INFO - DAG bundles loaded: dags-folder, 
example_dags$/u),
-    );
-  });
-
-  it("rebuilds a removed ungrouped row from log data", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-    await waitForLogs();
-
-    const firstRow = findRow("Log message source details");
-    const middleRow = findRow("Task started");
-    const lastRow = findRow("Done. Returned value was: None");
-
-    expect(firstRow).toBeDefined();
-    expect(middleRow).toBeDefined();
-    expect(lastRow).toBeDefined();
-
-    middleRow.remove();
-
-    const range = document.createRange();
-
-    range.setStart(firstRow, 0);
-    range.setEnd(lastRow, lastRow.childNodes.length);
-
-    const selection = { getRangeAt: () => range, isCollapsed: false, 
rangeCount: 1 } as unknown as Selection;
-    const clipboardData = makeClipboardData();
-    const copyEvent = withFakeSelection(selection, () => 
dispatchCopy(clipboardData));
-
-    expect(copyEvent.defaultPrevented).toBe(true);
-    expect(clipboardData.getData("text/plain").split("\n")).toContainEqual(
-      expect.stringMatching(/^\[.+\] INFO - Task started$/u),
-    );
-  });
-
-  it("rebuilds middle rows with the exact text mounted rows show on screen", 
async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-    await waitForLogs();
-
-    const firstRow = findRow("Log message source details");
-    const taskStartedRow = findRow("Task started");
-    const headerRow = findRow("Pre Execute");
-    const lastRow = findRow("Done. Returned value was: None");
-
-    const taskStartedScreenText = getRowCopyText(taskStartedRow);
-    const headerScreenText = getRowCopyText(headerRow);
-
-    expect(taskStartedScreenText).toMatch(/^\[\d{4}-\d{2}-\d{2} 
\d{2}:\d{2}:\d{2}\] INFO - Task started$/u);
-    expect(headerScreenText).toBe("▶ Pre Execute");
-
-    taskStartedRow.remove();
-
-    const range = document.createRange();
-
-    range.setStart(firstRow, 0);
-    range.setEnd(lastRow, lastRow.childNodes.length);
-
-    const selection = { getRangeAt: () => range, isCollapsed: false, 
rangeCount: 1 } as unknown as Selection;
-    const clipboardData = makeClipboardData();
-
-    withFakeSelection(selection, () => dispatchCopy(clipboardData));
-
-    const lines = clipboardData.getData("text/plain").split("\n");
-
-    expect(lines[0]).toBe("▶ Log message source details");
-    expect(lines).toContain(taskStartedScreenText);
-    expect(lines).toContain(headerScreenText);
-  });
-
-  it("copies the expanded marker for expanded group headers", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-    await waitForLogs();
-
-    fireEvent.click(screen.getByTestId("summary-Pre Execute"));
-    await waitFor(() => expect(getRowCopyText(findRow("Pre Execute"))).toBe("▼ 
Pre Execute"));
-
-    const firstRow = findRow("Log message source details");
-    const headerRow = findRow("Pre Execute");
-    const taskStartedRow = findRow("Task started");
-    const lastRow = findRow("Done. Returned value was: None");
-
-    taskStartedRow.remove();
-    headerRow.remove();
-
-    const range = document.createRange();
-
-    range.setStart(firstRow, 0);
-    range.setEnd(lastRow, lastRow.childNodes.length);
-
-    const selection = { getRangeAt: () => range, isCollapsed: false, 
rangeCount: 1 } as unknown as Selection;
-    const clipboardData = makeClipboardData();
-
-    withFakeSelection(selection, () => dispatchCopy(clipboardData));
-
-    expect(clipboardData.getData("text/plain").split("\n")).toContain("▼ Pre 
Execute");
-  });
-
-  it("leaves single-row selections to native copy", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-    await waitForLogs();
-
-    const row = findRow("Task started");
-
-    expect(row).toBeDefined();
-
-    const range = document.createRange();
-
-    range.setStart(row, 0);
-    range.setEnd(row, row.childNodes.length);
-
-    const selection = { getRangeAt: () => range, isCollapsed: false, 
rangeCount: 1 } as unknown as Selection;
-    const clipboardData = makeClipboardData();
-    const copyEvent = withFakeSelection(selection, () => 
dispatchCopy(clipboardData));
-
-    expect(copyEvent.defaultPrevented).toBe(false);
-    expect(clipboardData.getData("text/plain")).toBe("");
-  });
-});
-
-describe("Selection pinning across scrolling", () => {
-  it("keeps the selection-anchor row mounted after scrolling it out of the 
render window", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/generate"]}
 />,
-    );
-    await waitForLogs();
-
-    fireEvent.click(screen.getByTestId("summary-Pre task execution logs"));
-    await waitFor(() => expect(screen.getByText(/starting attempt 1 of 
3/iu)).toBeInTheDocument());
-
-    const anchorRow = findRow("Starting attempt 1 of 3");
-    const anchorIndex = Number(anchorRow.getAttribute("data-index"));
-    const neighborIndex = anchorIndex + 1;
-    const textNode = anchorRow.querySelector("span")?.firstChild as Node;
-    const range = document.createRange();
-
-    range.setStart(textNode, 0);
-    range.setEnd(textNode, 0);
-
-    const selection = { getRangeAt: () => range, isCollapsed: true, 
rangeCount: 1 } as unknown as Selection;
-
-    withFakeSelection(selection, () => {
-      document.dispatchEvent(new Event("selectionchange"));
-    });
-
-    const container = screen.getByTestId("virtual-scroll-container");
-
-    fireEvent.scroll(container, { target: { scrollTop: ITEM_HEIGHT * 
(anchorIndex + 15) } });
-
-    await waitFor(() => {
-      
expect(container.querySelector(`[data-index="${neighborIndex}"]`)).toBeNull();
-    });
-    
expect(container.querySelector(`[data-index="${anchorIndex}"]`)).not.toBeNull();
-  });
-
-  it("unpins once the selection is cleared", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/generate"]}
 />,
-    );
-    await waitForLogs();
-
-    fireEvent.click(screen.getByTestId("summary-Pre task execution logs"));
-    await waitFor(() => expect(screen.getByText(/starting attempt 1 of 
3/iu)).toBeInTheDocument());
-
-    const anchorRow = findRow("Starting attempt 1 of 3");
-    const anchorIndex = Number(anchorRow.getAttribute("data-index"));
-    const textNode = anchorRow.querySelector("span")?.firstChild as Node;
-    const range = document.createRange();
-
-    range.setStart(textNode, 0);
-    range.setEnd(textNode, 0);
-
-    const selection = { getRangeAt: () => range, isCollapsed: true, 
rangeCount: 1 } as unknown as Selection;
-
-    withFakeSelection(selection, () => {
-      document.dispatchEvent(new Event("selectionchange"));
-    });
-
-    const noSelection = null as unknown as Selection;
-
-    withFakeSelection(noSelection, () => {
-      document.dispatchEvent(new Event("selectionchange"));
-    });
-
-    const container = screen.getByTestId("virtual-scroll-container");
-
-    fireEvent.scroll(container, { target: { scrollTop: ITEM_HEIGHT * 
(anchorIndex + 15) } });
-
-    await waitFor(() => {
-      
expect(container.querySelector(`[data-index="${anchorIndex}"]`)).toBeNull();
-    });
-  });
-});
-
-describe("Downward drag selection", () => {
-  it("coalesces events and extends the selection to the mounted bottom row", 
async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-    await waitForLogs();
-
-    const container = screen.getByTestId("virtual-scroll-container");
-    const rows = container.querySelectorAll<HTMLElement>("[data-index]");
-    const anchorRow = rows[0] as HTMLElement;
-    const lastRow = rows[rows.length - 1] as HTMLElement;
-    const extend = vi.fn();
-    const range = document.createRange();
-
-    range.selectNodeContents(anchorRow);
-
-    const selection = {
-      anchorNode: anchorRow,
-      extend,
-      focusNode: anchorRow,
-      focusOffset: 0,
-      getRangeAt: () => range,
-      rangeCount: 1,
-    } as unknown as Selection;
-    const animationFrames = new Array<FrameRequestCallback>();
-    const getSelectionSpy = vi.spyOn(document, 
"getSelection").mockReturnValue(selection);
-    const requestAnimationFrameSpy = vi
-      .spyOn(globalThis, "requestAnimationFrame")
-      .mockImplementation((callback) => {
-        animationFrames.push(callback);
-
-        return animationFrames.length;
-      });
-    const cancelAnimationFrameSpy = vi
-      .spyOn(globalThis, "cancelAnimationFrame")
-      .mockImplementation(() => undefined);
-
-    container.getBoundingClientRect = () => ({ bottom: 500 }) as DOMRect;
-    lastRow.getBoundingClientRect = () => ({ bottom: 480 }) as DOMRect;
-
-    fireEvent.pointerDown(anchorRow, { button: 0, clientY: 200, pointerType: 
"mouse" });
-    fireEvent.pointerMove(document, { clientY: 490, pointerType: "mouse" });
-    document.dispatchEvent(new Event("selectionchange"));
-    fireEvent.scroll(container);
-
-    expect(animationFrames).toHaveLength(1);
-    animationFrames.shift()?.(0);
-    expect(extend).toHaveBeenCalledWith(lastRow, lastRow.childNodes.length);
-
-    fireEvent.scroll(container);
-    expect(animationFrames).toHaveLength(1);
-    animationFrames.shift()?.(1);
-    expect(extend).toHaveBeenCalledTimes(2);
-
-    fireEvent.scroll(container);
-    const pendingAnimationFrame = animationFrames.shift();
-
-    fireEvent.pointerUp(document, { pointerType: "mouse" });
-    expect(cancelAnimationFrameSpy).toHaveBeenCalledWith(1);
-
-    pendingAnimationFrame?.(2);
-    expect(extend).toHaveBeenCalledTimes(2);
-
-    getSelectionSpy.mockRestore();
-    requestAnimationFrameSpy.mockRestore();
-    cancelAnimationFrameSpy.mockRestore();
-  });
-
-  it("only activates for downward primary-mouse drags starting in a log row", 
async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-    await waitForLogs();
-
-    const container = screen.getByTestId("virtual-scroll-container");
-    const anchorRow = container.querySelector<HTMLElement>("[data-index]") as 
HTMLElement;
-    const rows = container.querySelectorAll<HTMLElement>("[data-index]");
-    const lastRow = rows[rows.length - 1] as HTMLElement;
-    const requestAnimationFrameSpy = vi
-      .spyOn(globalThis, "requestAnimationFrame")
-      .mockImplementation(() => 1);
-
-    container.getBoundingClientRect = () => ({ bottom: 500 }) as DOMRect;
-    lastRow.getBoundingClientRect = () => ({ bottom: 700 }) as DOMRect;
-
-    fireEvent.pointerDown(container, { button: 0, clientY: 200, pointerType: 
"mouse" });
-    fireEvent.pointerMove(document, { clientY: 600, pointerType: "mouse" });
-    expect(requestAnimationFrameSpy).not.toHaveBeenCalled();
-
-    fireEvent.pointerDown(anchorRow, { button: 2, clientY: 200, pointerType: 
"mouse" });
-    fireEvent.pointerMove(document, { clientY: 600, pointerType: "mouse" });
-    expect(requestAnimationFrameSpy).not.toHaveBeenCalled();
-
-    fireEvent.pointerDown(anchorRow, { button: 0, clientY: 200, pointerType: 
"touch" });
-    fireEvent.pointerMove(document, { clientY: 600, pointerType: "touch" });
-    expect(requestAnimationFrameSpy).not.toHaveBeenCalled();
-
-    fireEvent.pointerDown(anchorRow, { button: 0, clientY: 200, pointerType: 
"mouse" });
-    fireEvent.pointerMove(document, { clientY: 50, pointerType: "mouse" });
-    fireEvent.scroll(container);
-    expect(requestAnimationFrameSpy).not.toHaveBeenCalled();
-
-    fireEvent.pointerUp(document, { pointerType: "mouse" });
-    requestAnimationFrameSpy.mockRestore();
-  });
-});
diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/LogsSelection.test.tsx
similarity index 52%
copy from airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
copy to 
airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/LogsSelection.test.tsx
index e0da0bb4bd5..b48c89f5572 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.test.tsx
+++ 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/LogsSelection.test.tsx
@@ -50,395 +50,6 @@ const waitForLogs = async () => {
   fireEvent.scroll(screen.getByTestId("virtualized-list"), { target: { 
scrollTop: ITEM_HEIGHT * 2 } });
 };
 
-describe("Task log source", () => {
-  it("Toggles logger and location on click", async () => {
-    render(
-      // <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-09-11T17:44:49.064088+00:00/tasks/source_testing"]}
 />,
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/log_source"]}
 />,
-    );
-
-    await waitForLogs();
-
-    // Source should be hidden by default
-    expect(document.querySelector('[data-key="logger"]')).toBeNull();
-    expect(document.querySelector('[data-key="loc"]')).toBeNull();
-
-    // Toggle source on
-    fireEvent.keyDown(document.activeElement ?? document.body, { code: "KeyS", 
key: "S" });
-    fireEvent.keyPress(document.activeElement ?? document.body, { code: 
"KeyS", key: "S" });
-    fireEvent.keyUp(document.activeElement ?? document.body, { code: "KeyS", 
key: "S" });
-
-    const dagBagRow = (await screen.findByText(/Filling up the 
DagBag/iu)).closest(
-      '[data-testid^="virtualized-item-"]',
-    );
-    const source = dagBagRow?.querySelector('[data-key="logger"]') ?? 
undefined;
-    const loc = dagBagRow?.querySelector('[data-key="loc"]') ?? undefined;
-
-    // Source should now be visible
-    expect(source).toBeVisible();
-    expect(source).toHaveProperty("innerText", 
"source=airflow.models.dagbag.DagBag");
-
-    expect(loc).toBeVisible();
-    expect(loc).toHaveProperty("innerText", "loc=dagbag.py:593");
-  });
-});
-describe("Task log grouping", () => {
-  it("Display task log content on click", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/generate"]}
 />,
-    );
-
-    await waitForLogs();
-
-    // Group headers use the summary-{name} testid pattern and are always 
visible
-    const summarySource = screen.getByTestId("summary-Log message source 
details");
-
-    expect(summarySource).toBeVisible();
-
-    const summaryPre = screen.getByTestId("summary-Pre task execution logs");
-
-    expect(summaryPre).toBeVisible();
-
-    // All groups start collapsed. Verify Post header is in the virtualizer 
range.
-    const summaryPost = screen.getByTestId("summary-Post task execution logs");
-
-    expect(summaryPost).toBeVisible();
-
-    // Groups start collapsed — content should not be in DOM
-    expect(screen.queryByText(/starting attempt 1 of 3/iu)).toBeNull();
-
-    // Click to expand Pre
-    fireEvent.click(summaryPre);
-    await waitFor(() => expect(screen.getByText(/starting attempt 1 of 
3/iu)).toBeInTheDocument());
-
-    // Click Pre to collapse
-    fireEvent.click(summaryPre);
-    await waitFor(() => expect(screen.queryByText(/Task instance is in running 
state/iu)).toBeNull());
-
-    // Click Pre to expand again
-    fireEvent.click(summaryPre);
-    await waitFor(() =>
-      expect(screen.queryByText(/Task instance is in running 
state/iu)).toBeInTheDocument(),
-    );
-
-    // Click Pre to collapse again (to return to compact view)
-    fireEvent.click(summaryPre);
-    await waitFor(() => expect(screen.queryByText(/Task instance is in running 
state/iu)).toBeNull());
-
-    // Now expand Post (which is visible because Pre is collapsed again)
-    fireEvent.click(screen.getByTestId("summary-Post task execution logs"));
-    await waitFor(() => expect(screen.queryByText(/Marking task as 
SUCCESS/iu)).toBeInTheDocument());
-
-    // Collapse Post
-    fireEvent.click(screen.getByTestId("summary-Post task execution logs"));
-    await waitFor(() => expect(screen.queryByText(/Marking task as 
SUCCESS/iu)).toBeNull());
-
-    // Test Expand All / Collapse All via settings menu
-    const settingsBtn = screen.getByTestId("log-settings-button");
-
-    fireEvent.click(settingsBtn);
-
-    const expandItem = await screen.findByRole("menuitem", { name: /expand/iu 
});
-
-    fireEvent.click(expandItem);
-
-    // After "Expand All", Pre group content near the top should be visible
-    await waitFor(() => expect(screen.queryByText(/starting attempt 1 of 
3/iu)).toBeInTheDocument());
-
-    /* ─── Click  "Collapse"  ─── */
-    fireEvent.click(settingsBtn);
-    const collapseItem = await screen.findByRole("menuitem", { name: 
/collapse/iu });
-
-    fireEvent.click(collapseItem);
-
-    // After "Collapse All", group content should be gone from the DOM
-    await waitFor(() => expect(screen.queryByText(/starting attempt 1 of 
3/iu)).toBeNull());
-  }, 10_000);
-
-  it("renders nested groups correctly", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/generate"]}
 />,
-    );
-
-    await waitForLogs();
-
-    // The nested group "Dependency check details" should have a header
-    // First expand the parent group "Pre task execution logs"
-    const summaryPre = screen.getByTestId("summary-Pre task execution logs");
-
-    fireEvent.click(summaryPre);
-
-    // The nested group header should now be visible
-    await waitFor(() => expect(screen.getByTestId("summary-Dependency check 
details")).toBeInTheDocument());
-
-    // But nested group content is collapsed
-    expect(screen.queryByText(/dep_context=non-requeueable/iu)).toBeNull();
-
-    // Expand the nested group
-    fireEvent.click(screen.getByTestId("summary-Dependency check details"));
-    await waitFor(() => 
expect(screen.getByText(/dep_context=non-requeueable/iu)).toBeInTheDocument());
-  }, 10_000);
-});
-
-describe("Task Identity preamble", () => {
-  it("renders Task Identity preamble after the 'Pre Execute' group header as 
first group element", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const sourceGroup = screen.getByTestId("summary-Log message source 
details");
-
-    expect(sourceGroup).toBeInTheDocument();
-
-    // Expand the Pre Execute group to reveal the preamble
-    const groupHeader = screen.getByTestId("summary-Pre Execute");
-
-    fireEvent.click(groupHeader);
-
-    // Task Identity preamble should be visible after expanding the group
-    await waitFor(() => expect(screen.getByText("Task 
Identity")).toBeInTheDocument());
-    expect(screen.getByText("ti_id")).toBeInTheDocument();
-    // Value is a text node adjacent to =; match via partial text
-    
expect(screen.getByText(/01951900-16f6-7c1c-ae66-91bdfe9e0cfd/u)).toBeInTheDocument();
-    expect(screen.getByText("Done. Returned value was: 
None")).toBeInTheDocument();
-
-    // Preamble should come after the "Pre Execute" group header in DOM order.
-    const preamble = screen.getByText("Task Identity");
-
-    expect(preamble).toBeInTheDocument();
-    expect(groupHeader).toBeInTheDocument();
-
-    // DOCUMENT_POSITION_FOLLOWING (4) is set when preamble comes after 
groupHeader
-    // eslint-disable-next-line no-bitwise
-    expect(groupHeader.compareDocumentPosition(preamble) & 
Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
-  });
-
-  it("does not render TI context fields on individual log lines", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/ti_context"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const taskStarted = screen.getByText("Task 
started").closest('[data-testid^="virtualized-item-"]');
-
-    expect(taskStarted).toBeInTheDocument();
-
-    if (taskStarted !== null) {
-      expect(taskStarted.querySelector('[data-key="ti_id"]')).toBeNull();
-      expect(taskStarted.querySelector('[data-key="dag_id"]')).toBeNull();
-      expect(taskStarted.querySelector('[data-key="run_id"]')).toBeNull();
-    }
-  });
-});
-
-describe("Task log search", () => {
-  it("search input is rendered in the log header", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/log_source"]}
 />,
-    );
-
-    await waitForLogs();
-
-    expect(screen.getByTestId("log-search-input")).toBeInTheDocument();
-  });
-
-  it("typing in the search input enables navigation buttons for a known term", 
async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/log_source"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const searchInput = screen.getByTestId("log-search-input");
-
-    // "running state" appears in the mock log data
-    fireEvent.change(searchInput, { target: { value: "running state" } });
-
-    // Navigation buttons should become enabled once matches are found
-    await waitFor(() => {
-      expect(screen.getByRole("button", { name: /next match/iu 
})).not.toBeDisabled();
-      expect(screen.getByRole("button", { name: /previous match/iu 
})).not.toBeDisabled();
-    });
-  });
-
-  it("shows no-matches indicator for a term that does not exist in logs", 
async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/log_source"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const searchInput = screen.getByTestId("log-search-input");
-
-    fireEvent.change(searchInput, { target: { value: "zzz_not_in_logs_zzz" } 
});
-
-    await waitFor(() => {
-      // Navigation buttons should be disabled with zero matches
-      const nextBtn = screen.getByRole("button", { name: /next match/iu });
-
-      expect(nextBtn).toBeDisabled();
-    });
-  });
-
-  it("pressing Escape clears the search query", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/log_source"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const searchInput = screen.getByTestId("log-search-input");
-
-    fireEvent.change(searchInput, { target: { value: "running" } });
-
-    await waitFor(() => expect(screen.queryByRole("button", { name: /next 
match/iu })).toBeInTheDocument());
-
-    fireEvent.keyDown(searchInput, { key: "Escape" });
-
-    await waitFor(() => {
-      expect((searchInput as HTMLInputElement).value).toBe("");
-    });
-  });
-
-  it("pressing Enter keeps navigation buttons enabled (navigates to next 
match)", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/log_source"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const searchInput = screen.getByTestId("log-search-input");
-
-    // "state" appears multiple times in the mock log data
-    fireEvent.change(searchInput, { target: { value: "state" } });
-
-    // Wait for matches to be found (navigation buttons become enabled)
-    await waitFor(() => expect(screen.getByRole("button", { name: /next 
match/iu })).not.toBeDisabled());
-
-    // Navigate forward — buttons remain enabled
-    fireEvent.keyDown(searchInput, { key: "Enter" });
-
-    await waitFor(() => {
-      expect(screen.getByRole("button", { name: /next match/iu 
})).not.toBeDisabled();
-    });
-  });
-
-  it("pressing Shift+Enter keeps navigation buttons enabled (navigates to 
previous match)", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/log_source"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const searchInput = screen.getByTestId("log-search-input");
-
-    fireEvent.change(searchInput, { target: { value: "state" } });
-
-    // Wait for matches to be found
-    await waitFor(() => expect(screen.getByRole("button", { name: /previous 
match/iu })).not.toBeDisabled());
-
-    // Navigate backward — buttons remain enabled
-    fireEvent.keyDown(searchInput, { key: "Enter", shiftKey: true });
-
-    await waitFor(() => {
-      expect(screen.getByRole("button", { name: /previous match/iu 
})).not.toBeDisabled();
-    });
-  });
-
-  it("search finds matches per-line inside log groups (not collapsed into 1 
match per group)", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/generate"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const searchInput = screen.getByTestId("log-search-input");
-
-    // "INFO" appears in many individual log lines across multiple groups.
-    // With the old code, each group collapsed into 1 match. Now each line is 
a separate match.
-    fireEvent.change(searchInput, { target: { value: "INFO" } });
-
-    await waitFor(() => {
-      expect(screen.getByRole("button", { name: /next match/iu 
})).not.toBeDisabled();
-    });
-
-    // Verify per-line search by navigating through matches.
-    // With the old code (1 match per group), there were only 3 matches for 
"INFO".
-    // Now each line is a separate match, so we can navigate through many more.
-    const nextBtn = screen.getByRole("button", { name: /next match/iu });
-
-    // Navigate forward 4 times — if only 3 matches existed, the 4th click 
would wrap to #1
-    // We just verify it stays enabled (which it always does with >0 matches).
-    // The real proof is that the auto-expand test passes: searching "starting 
attempt"
-    // finds a match INSIDE a group, which was impossible with the old 
collapsed approach.
-    for (let navStep = 0; navStep < 4; navStep += 1) {
-      fireEvent.click(nextBtn);
-    }
-
-    await waitFor(() => {
-      expect(nextBtn).not.toBeDisabled();
-    });
-  }, 10_000);
-
-  it("search navigating to match in collapsed group auto-expands it", async () 
=> {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/generate"]}
 />,
-    );
-
-    await waitForLogs();
-
-    // All groups start collapsed. Search for text that only appears inside a 
group.
-    const searchInput = screen.getByTestId("log-search-input");
-
-    fireEvent.change(searchInput, { target: { value: "starting attempt" } });
-
-    await waitFor(() => {
-      const nextBtn = screen.getByRole("button", { name: /next match/iu });
-
-      expect(nextBtn).not.toBeDisabled();
-    });
-
-    // The match is inside "Pre task execution logs" group.
-    // Auto-expand should make it visible.
-    await waitFor(() => expect(screen.getByText(/starting attempt 1 of 
3/iu)).toBeInTheDocument());
-  }, 10_000);
-
-  it("skips group markers when assigning line numbers", async () => {
-    render(
-      <AppWrapper 
initialEntries={["/dags/log_grouping/runs/manual__2025-02-18T12:19/tasks/generate"]}
 />,
-    );
-
-    await waitForLogs();
-
-    const expectRenderedLineNumber = async (pattern: RegExp, 
expectedLineNumber: number) => {
-      const row = (await 
screen.findByText(pattern)).closest('[data-testid^="virtualized-item-"]');
-
-      expect(row).not.toBeNull();
-
-      const anchor = row?.querySelector<HTMLAnchorElement>("a[id]");
-
-      expect(anchor).not.toBeNull();
-
-      expect(Number(anchor?.id)).toBe(expectedLineNumber);
-    };
-
-    const summaryPre = screen.getByTestId("summary-Pre task execution logs");
-
-    fireEvent.click(summaryPre);
-
-    const summaryDependency = await screen.findByTestId("summary-Dependency 
check details");
-
-    fireEvent.click(summaryDependency);
-
-    await expectRenderedLineNumber(/dep_context=non-requeueable/iu, 1);
-    await expectRenderedLineNumber(/dep_context=requeueable/iu, 2);
-    await expectRenderedLineNumber(/starting attempt 1 of 3/iu, 3);
-  }, 10_000);
-});
-
 const makeClipboardData = () => {
   const store = new Map<string, string>();
 

Reply via email to