Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ yarn-debug.log*
yarn-error.log*

.eslintcache
CLAUDE.md
43 changes: 43 additions & 0 deletions src/__tests__/CompareResults/FilteredRowsNotice.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import FilteredRowsNotice from '../../components/CompareResults/FilteredRowsNotice';
import type { ActiveColumnFilter } from '../../hooks/useTableFilters';
import { render, screen } from '../utils/test-utils';

describe('FilteredRowsNotice', () => {
const filters: ActiveColumnFilter[] = [
{ name: 'Significance', excludedLabels: ['Noise'] },
];

it('renders nothing when no rows are hidden', () => {
const { container } = render(
<FilteredRowsNotice hiddenCount={0} activeFilters={filters} />,
);
expect(container).toBeEmptyDOMElement();
expect(
screen.queryByTestId('filtered-rows-notice'),
).not.toBeInTheDocument();
});

it('reports the count (singular) and the active filter reasons', () => {
render(<FilteredRowsNotice hiddenCount={1} activeFilters={filters} />);
const notice = screen.getByTestId('filtered-rows-notice');
expect(notice).toHaveTextContent('1 row hidden by filters');
expect(notice).toHaveTextContent('Significance: Noise');
});

it('pluralizes the count and joins multiple filters and values', () => {
render(
<FilteredRowsNotice
hiddenCount={3}
activeFilters={[
{ name: 'Significance', excludedLabels: ['Noise'] },
{ name: 'Status', excludedLabels: ['No changes', 'Improvement'] },
]}
/>,
);
const notice = screen.getByTestId('filtered-rows-notice');
expect(notice).toHaveTextContent('3 rows hidden by filters');
expect(notice).toHaveTextContent(
'Significance: Noise • Status: No changes, Improvement',
);
});
});
111 changes: 109 additions & 2 deletions src/__tests__/CompareResults/ResultsTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1308,7 +1308,7 @@ describe('Advanced-columns toggle for mann-whitney-u testVersion', () => {

// Open the dropdown and enable Cliff's Delta only.
await user.click(
screen.getByRole('combobox', { name: 'Advanced columns' }),
screen.getByRole('combobox', { name: 'Advanced options' }),
);
await user.click(screen.getByRole('option', { name: "Cliff's Delta" }));
expect(header().querySelector('.delta-header')).toBeTruthy();
Expand Down Expand Up @@ -1349,7 +1349,7 @@ describe('Advanced-columns toggle for mann-whitney-u testVersion', () => {
expect(advancedParam()).toBeNull();

await user.click(
screen.getByRole('combobox', { name: 'Advanced columns' }),
screen.getByRole('combobox', { name: 'Advanced options' }),
);
await user.click(screen.getByRole('option', { name: "Cliff's Delta" }));
expect(advancedParam()).toBe('cliffs_delta');
Expand All @@ -1364,6 +1364,76 @@ describe('Advanced-columns toggle for mann-whitney-u testVersion', () => {
await user.click(screen.getByRole('option', { name: 'CLES' }));
expect(advancedParam()).toBeNull();
});

it('groups the dropdown into Columns and Expanded row sections', async () => {
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
const { testCompareMannWhitneyData } = getTestData();
setupAndRender(testCompareMannWhitneyData, 'test_version=mann-whitney-u');
await screen.findByText('a11yr');

await user.click(
screen.getByRole('combobox', { name: 'Advanced options' }),
);

// Both group headers and one option from each group are present.
expect(screen.getByText('Columns')).toBeInTheDocument();
expect(screen.getByText('Expanded row')).toBeInTheDocument();
expect(
screen.getByRole('option', { name: "Cliff's Delta" }),
).toBeInTheDocument();
expect(
screen.getByRole('option', { name: 'Statistics table' }),
).toBeInTheDocument();
});

it('seeds the expanded-row selection from the advanced_expanded URL param', async () => {
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
const { testCompareMannWhitneyData } = getTestData();
setupAndRender(
testCompareMannWhitneyData,
'test_version=mann-whitney-u&advanced_expanded=stats_table',
);
await screen.findByText('a11yr');

await user.click(
screen.getByRole('combobox', { name: 'Advanced options' }),
);

// The seeded option is checked; an unseeded one is not.
expect(
screen.getByRole('option', { name: 'Statistics table' }),
).toHaveAttribute('aria-selected', 'true');
expect(
screen.getByRole('option', { name: 'Data warnings' }),
).toHaveAttribute('aria-selected', 'false');
});

it('persists the expanded-row selection to the advanced_expanded URL param', async () => {
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
const { testCompareMannWhitneyData } = getTestData();
setupAndRender(testCompareMannWhitneyData, 'test_version=mann-whitney-u');
await screen.findByText('a11yr');

const expandedParam = () =>
new URLSearchParams(window.location.search).get('advanced_expanded');
expect(expandedParam()).toBeNull();

await user.click(
screen.getByRole('combobox', { name: 'Advanced options' }),
);
await user.click(screen.getByRole('option', { name: 'Statistics table' }));
expect(expandedParam()).toBe('stats_table');

await user.click(screen.getByRole('option', { name: 'Data warnings' }));
expect(expandedParam()).toBe('stats_table,warnings');

// Turning the columns off leaves the expanded-row param untouched.
await user.click(screen.getByRole('option', { name: 'Statistics table' }));
expect(expandedParam()).toBe('warnings');

await user.click(screen.getByRole('option', { name: 'Data warnings' }));
expect(expandedParam()).toBeNull();
});
});

describe('cookie persistence vs. shareable URLs', () => {
Expand Down Expand Up @@ -1483,3 +1553,40 @@ describe('cookie persistence vs. shareable URLs', () => {
});
});
});

// Placed at the end of the file on purpose: this test performs extra renders /
// menu interactions, and React's useId counter is global across renders in a
// jest run, so running it before the snapshot tests above would shift their
// generated ids. Keeping it last avoids perturbing those snapshots.
describe('Filtered-rows notice', () => {
it('shows how many rows the active filters hide, and why', async () => {
const { testCompareData } = getTestData();
setupAndRender(testCompareData, 'test_version=student-t');

await screen.findByText('a11yr');
// No active filters → no notice.
expect(
screen.queryByTestId('filtered-rows-notice'),
).not.toBeInTheDocument();

const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });

// Hide the two "No changes" rows; the notice reports the count and reason.
await clickMenuItem(user, 'Status', /No changes/);
const notice = await screen.findByTestId('filtered-rows-notice');
expect(notice).toHaveTextContent('2 rows hidden by filters');
expect(notice).toHaveTextContent('Status: No changes');

// Narrowing to a single status hides more rows and the count updates.
await clickMenuItem(user, 'Status', /Select only.*Regression/);
expect(await screen.findByTestId('filtered-rows-notice')).toHaveTextContent(
'3 rows hidden by filters',
);

// Clearing the filter removes the notice again.
await clickMenuItem(user, 'Status', /Select all values/);
expect(
screen.queryByTestId('filtered-rows-notice'),
).not.toBeInTheDocument();
});
});
5 changes: 5 additions & 0 deletions src/__tests__/CompareResults/RevisionRow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
screen,
renderWithRouter,
enableAdvancedColumns,
enableExpandedRowOptions,
} from '../utils/test-utils';

jest.mock('../../hooks/useSubtestRegressionCount');
Expand Down Expand Up @@ -167,6 +168,10 @@ describe('Subtest count pills', () => {
});

describe('Expanded row', () => {
// Several tests assert the (advanced) MWU expanded-row components, which are
// hidden by default; enable them. No-op for Student-T expanded rows.
beforeEach(() => enableExpandedRowOptions());

it('should display "Show 39 more" and "Show less" for base runs when row is expanded', async () => {
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
const { testCompareDataWithReplicatesMultipleValues: rowData } =
Expand Down
112 changes: 111 additions & 1 deletion src/__tests__/CompareResults/RevisionRowExpandable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import fetchMock from '@fetch-mock/jest';
import { loader } from '../../components/CompareResults/loader';
import RevisionRowExpandable from '../../components/CompareResults/RevisionRowExpandable';
import getTestData from '../utils/fixtures';
import { screen, renderWithRouter } from '../utils/test-utils';
import {
screen,
renderWithRouter,
enableExpandedRowOptions,
} from '../utils/test-utils';

function renderWithRoute(component: ReactElement) {
fetchMock
Expand Down Expand Up @@ -62,6 +66,9 @@ describe('RevisionRowExpandable for student-t testVersion', () => {
});

describe('RevisionRowExpandable for mann-whitney-u testVersion', () => {
// These assert the (advanced) expanded-row components, hidden by default.
beforeEach(() => enableExpandedRowOptions());

it('should display warnings', async () => {
const { mockMannWhitneyResultData } = getTestData();

Expand Down Expand Up @@ -147,3 +154,106 @@ describe('RevisionRowExpandable for mann-whitney-u testVersion', () => {
expect(goodFit).toBeInTheDocument();
});
});

describe('RevisionRowExpandable simplified Mann-Whitney-U view', () => {
// Text unique to each advanced expanded-row component, used to assert whether
// that component is currently rendered.
const EFFECT_SIZE_TEXT = /Effect Size:/;
const STATS_TABLE_TEXT = /Normality Test/;
const WARNINGS_TEXT = /Shapiro-Wilk test cannot be run/;
const MODES_LABEL = 'Mode-by-mode breakdown';

function renderMwuRow() {
const { mockMannWhitneyResultData } = getTestData();
renderWithRoute(
<RevisionRowExpandable
result={mockMannWhitneyResultData}
testVersion='mann-whitney-u'
id='mwu-simple'
/>,
);
}

it('shows the graph blurb and hides every advanced component by default', async () => {
renderMwuRow();

// The how-to-read blurb is always present in the simplified view.
expect(
await screen.findByText(/how the Base and New results are distributed/i),
).toBeInTheDocument();

// None of the advanced (checkbox-gated) components render by default.
expect(screen.queryByText(EFFECT_SIZE_TEXT)).not.toBeInTheDocument();
expect(screen.queryByText(STATS_TABLE_TEXT)).not.toBeInTheDocument();
expect(screen.queryByText(WARNINGS_TEXT)).not.toBeInTheDocument();
expect(screen.queryByLabelText(MODES_LABEL)).not.toBeInTheDocument();
});

it('reveals only the effect size component when that option is on', async () => {
enableExpandedRowOptions({ effectSize: true });
renderMwuRow();

expect(await screen.findByText(EFFECT_SIZE_TEXT)).toBeInTheDocument();
expect(screen.queryByText(STATS_TABLE_TEXT)).not.toBeInTheDocument();
expect(screen.queryByText(WARNINGS_TEXT)).not.toBeInTheDocument();
});

it('reveals only the statistics table when that option is on', async () => {
enableExpandedRowOptions({ statsTable: true });
renderMwuRow();

expect(await screen.findByText(STATS_TABLE_TEXT)).toBeInTheDocument();
expect(screen.queryByText(EFFECT_SIZE_TEXT)).not.toBeInTheDocument();
expect(screen.queryByText(WARNINGS_TEXT)).not.toBeInTheDocument();
});

it('reveals only the data warnings when that option is on', async () => {
enableExpandedRowOptions({ warnings: true });
renderMwuRow();

// Base and New each contribute a Shapiro-Wilk warning.
expect((await screen.findAllByText(WARNINGS_TEXT)).length).toBeGreaterThan(
0,
);
expect(screen.queryByText(EFFECT_SIZE_TEXT)).not.toBeInTheDocument();
expect(screen.queryByText(STATS_TABLE_TEXT)).not.toBeInTheDocument();
});

it('hides the mode-analysis controls (valley-depth slider + Show modes) by default', async () => {
renderMwuRow();
await screen.findByText(/how the Base and New results are distributed/i);

expect(
screen.queryByRole('slider', { name: /valley depth threshold/i }),
).not.toBeInTheDocument();
expect(
screen.queryByRole('checkbox', { name: /show modes/i }),
).not.toBeInTheDocument();
});

it('reveals the mode-analysis controls when Mode analysis is enabled', async () => {
enableExpandedRowOptions({ modes: true });
renderMwuRow();

expect(
await screen.findByRole('slider', { name: /valley depth threshold/i }),
).toBeInTheDocument();
expect(
screen.getByRole('checkbox', { name: /show modes/i }),
).toBeInTheDocument();
});

it('shows a placeholder when Mode analysis is on but no breakdown is available', async () => {
// The mock is a sparse/unimodal comparison, so no mode-by-mode breakdown is
// produced; the cell should surface a placeholder rather than stay empty.
enableExpandedRowOptions({ modes: true });
renderMwuRow();

expect(
await screen.findByText(/No mode analysis available/i),
).toBeInTheDocument();
expect(
screen.queryByLabelText('Mode-by-mode breakdown'),
).not.toBeInTheDocument();
});
});
7 changes: 7 additions & 0 deletions src/__tests__/CompareResults/SubtestsResultsView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ import {
renderWithRouter,
screen,
enableAdvancedColumns,
enableExpandedRowOptions,
} from '../utils/test-utils';

jest.mock('../../utils/location');
const mockedGetLocationOrigin = getLocationOrigin as jest.Mock;

// The Mann-Whitney-U expanded row hides its statistical components (stats
// table, warnings, effect size, modes) behind "Advanced options" by default.
// These tests assert on those components, so enable them everywhere here. This
// is a no-op for rows that aren't expanded and for the Student-T layout.
beforeEach(() => enableExpandedRowOptions());

const setup = ({
element,
route,
Expand Down
3 changes: 3 additions & 0 deletions src/__tests__/CompareResults/SubtestsRevisionRow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
screen,
renderWithRouter,
enableAdvancedColumns,
enableExpandedRowOptions,
} from '../utils/test-utils';

function renderWithRoute(component: ReactElement) {
Expand Down Expand Up @@ -124,6 +125,7 @@ describe('SubtestsRevisionRow Component', () => {
});

it('renders subtests results with mann-whitney-u testVersion', async () => {
enableExpandedRowOptions();
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
const { subtestsResult } = getTestData();
const mockGridTemplateColumns = '1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr';
Expand All @@ -145,6 +147,7 @@ describe('SubtestsRevisionRow Component', () => {
});

it('renders subtests results defaulting to mann-whitney-u with no testVersion', async () => {
enableExpandedRowOptions();
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
const { subtestsMannWhitneyResult } = getTestData();
const mockGridTemplateColumns = '1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr';
Expand Down
Loading