Skip to content
Merged
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
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,18 @@ If you do not receive a response within 2-3 days, you can follow up in the #Perf

After addressing the issue, ensure both tests and linting pass before submitting a pull request.

When submitting a pull request, please mention the issue number to link the pull request and issue to one another. You can do this by typing # following immediately by the issue number, i.e., `#123`
When submitting a pull request, please mention the bugzilla issue number in the PR title and description . For example, if you are creating a PR for issue XXX, create a PR as below

```
Bug-XXX: Short description of the issue being fixed

Description of the issue being fixed and how the PR addresses it.

Fixes [Bug-XXX](https://bugzilla.mozilla.org/show_bug.cgi?id=XXX)
```

Once the PR is created, please link the PR to the corresponding bugzilla issue by pasting the PR link as an
attachment in the bugzilla issue.

---

Expand Down
2,031 changes: 1,029 additions & 1,002 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"buffer": "^6.0.3",
"chart.js": "^4.5.1",
"crypto-browserify": "^3.12.1",
"dayjs": "^1.11.19",
"dayjs": "^1.11.20",
"express": "^5.2.1",
"fast-kde": "^0.2.2",
"format": "^0.2.2",
Expand Down Expand Up @@ -83,13 +83,13 @@
"devDependencies": {
"@babel/core": "^7.29.0",
"@babel/plugin-transform-runtime": "^7.29.0",
"@babel/preset-env": "^7.29.0",
"@babel/preset-env": "^7.29.2",
"@babel/preset-react": "^7.28.5",
"@babel/preset-typescript": "^7.28.5",
"@babel/runtime": "^7.28.6",
"@babel/runtime": "^7.29.2",
"@eslint/js": "^9.39.4",
"@fetch-mock/jest": "^0.2.20",
"@jest/types": "^30.2.0",
"@jest/types": "^30.3.0",
"@swc/core": "^1.15.18",
"@swc/jest": "^0.2.39",
"@testing-library/dom": "^10.4.1",
Expand All @@ -99,14 +99,14 @@
"@types/jest": "^30.0.0",
"@types/jest-axe": "^3.5.9",
"@types/material-ui": "^0.21.18",
"@types/node": "^25.2.3",
"@types/node": "^25.5.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"babel-loader": "^10.1.1",
"babel-preset-jest": "^30.2.0",
"babel-preset-jest": "^30.3.0",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"copy-webpack-plugin": "^13.0.1",
"core-js": "^3.48.0",
"core-js": "^3.49.0",
"css-loader": "^7.1.4",
"eslint": "^9.39.4",
"eslint-config-prettier": "^10.1.8",
Expand All @@ -119,10 +119,10 @@
"globals": "^17.4.0",
"history": "^5.3.0",
"html-webpack-plugin": "^5.6.6",
"jest": "^30.2.0",
"jest": "^30.3.0",
"jest-axe": "^10.0.0",
"jest-environment-jsdom": "^30.2.0",
"jest-resolve": "^30.2.0",
"jest-environment-jsdom": "^30.3.0",
"jest-resolve": "^30.3.0",
"npm-run-all2": "^8.0.4",
"prettier": "3.7.4",
"react-app-polyfill": "^3.0.0",
Expand All @@ -133,7 +133,7 @@
"ts-loader": "^9.5.4",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.0",
"typescript-eslint": "^8.57.2",
"webpack": "^5.105.4",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.3",
Expand Down
40 changes: 40 additions & 0 deletions src/__tests__/CompareResults/RevisionRowExpandable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,46 @@ function renderWithRoute(component: ReactElement) {
});
}

describe('RevisionRowExpandable for student-t testVersion', () => {
it('should display median difference and percentage when both median values are present', async () => {
const { testCompareData } = getTestData();
// testCompareData[0]: base_median_value=704.84, new_median_value=712.44, unit='ms'
// expected difference: 7.6, percentage: 1.08
renderWithRoute(
<RevisionRowExpandable
result={testCompareData[0]}
testVersion='student-t'
id='test-1'
/>,
);

const header = await screen.findByText(/Difference of medians/);
const medianBox = header.closest('div');
expect(medianBox).toHaveTextContent('1.08%');
expect(medianBox).toHaveTextContent('7.6 ms');
});

it('should not display median difference when median values are absent', async () => {
const { testCompareData } = getTestData();
const noMedians = {
...testCompareData[0],
base_median_value: 0,
new_median_value: 0,
};

renderWithRoute(
<RevisionRowExpandable
result={noMedians}
testVersion='student-t'
id='test-2'
/>,
);

await screen.findByText(/Difference of means/);
expect(screen.queryByText(/Difference of medians/)).not.toBeInTheDocument();
});
});

describe('RevisionRowExpandable for mann-whitney-u testVersion', () => {
it('should display ModeInterpretation for mann-whitney-u', async () => {
const { mockMannWhitneyResultData } = getTestData();
Expand Down
9 changes: 5 additions & 4 deletions src/__tests__/CompareResults/SubtestsRevisionRow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,21 +139,21 @@ describe('SubtestsRevisionRow Component', () => {
expect(await screen.findByText(/Normality Test/i)).toBeInTheDocument();
});

it('renders subtests results defaulting to student-t with no testVersion', async () => {
it('renders subtests results defaulting to mann-whitney-u with no testVersion', async () => {
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
const { subtestsResult } = getTestData();
const { subtestsMannWhitneyResult } = getTestData();
const mockGridTemplateColumns = '1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr';
renderWithRoute(
<SubtestsRevisionRow
result={subtestsResult[0]}
result={subtestsMannWhitneyResult[0]}
gridTemplateColumns={mockGridTemplateColumns}
replicates={false}
/>,
);
const expandRowButton = await screen.findByTestId(/ExpandMoreIcon/);
await user.click(expandRowButton);

expect(await screen.findByText(/Difference of means/i)).toBeInTheDocument();
expect(await screen.findByText(/Normality Test/i)).toBeInTheDocument();
});

it('should display baseMean and newMean in subtests for student-t testVersion', async () => {
Expand All @@ -164,6 +164,7 @@ describe('SubtestsRevisionRow Component', () => {
result={subtestsResult[0]}
gridTemplateColumns={mockGridTemplateColumns}
replicates={false}
testVersion='student-t'
/>,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ exports[`Results View Should display Base, New and Common graphs with replicates
<b>
Comparison result
</b>
:

:

(
lower
Expand Down Expand Up @@ -460,8 +459,7 @@ exports[`Results View Should display Base, New and Common graphs with tooltips 1
<b>
Comparison result
</b>
:

:

(
lower
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ exports[`SubtestsRevisionRow Component renders browser name when base and new ap
&lt;
</div>
<div
class="mann-witney-browser-name cell"
class="browser-name cell"
role="cell"
>
984.16
Expand Down Expand Up @@ -208,7 +208,7 @@ exports[`SubtestsRevisionRow Component renders doesnt render browser name when b
&lt;
</div>
<div
class="mann-witney-browser-name cell"
class="browser-name cell"
role="cell"
>
982.41
Expand Down Expand Up @@ -362,7 +362,7 @@ exports[`SubtestsRevisionRow Component renders the component with correct data 1
&lt;
</div>
<div
class="mann-witney-browser-name cell"
class="browser-name cell"
role="cell"
>
982.41
Expand Down
8 changes: 8 additions & 0 deletions src/common/testVersions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ export interface TestVersionStrategy {
newAvg: number | null;
};
renderColumns(result: CombinedResultsItemType): ReactNode;
renderSubtestColumns(
result: CombinedResultsItemType,
expanded: boolean,
): ReactNode;
renderExpandedLeft(result: CombinedResultsItemType): ReactNode;
getComparisonResult(result: CombinedResultsItemType): string;
renderExpandedRight(result: CombinedResultsItemType): ReactNode;
renderExpandedBottom(result: CombinedResultsItemType): ReactNode;
}

// Registry mapping each TestVersion to its concrete strategy.
Expand Down
138 changes: 136 additions & 2 deletions src/common/testVersions/mannWhitney.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@ import ThumbDownIcon from '@mui/icons-material/ThumbDown';
import ThumbUpIcon from '@mui/icons-material/ThumbUp';
import Box from '@mui/material/Box';

import { MannWhitneyCompareMetrics } from '../../components/CompareResults/MannWhitneyCompareMetrics';
import { ModeInterpretation } from '../../components/CompareResults/ModeInterpretation';
import PValCliffsDeltaComp from '../../components/CompareResults/PValCliffsDeltaComp';
import { StatisticsWarnings } from '../../components/CompareResults/StatisticsWarnings';
import { FontSize } from '../../styles';
import {
CombinedResultsItemType,
MannWhitneyResultsItem,
} from '../../types/state';
import { TableConfig } from '../../types/types';
import { formatNumber } from '../../utils/format';
import { capitalize } from '../../utils/helpers';
import { getPlatformShortName } from '../../utils/platform';
import { determineStatusHintClass } from '../../utils/revisionRowHelpers';
import { getBrowserDisplay, getPlatformShortName } from '../../utils/platform';
import {
determineSign,
determineStatusHintClass,
} from '../../utils/revisionRowHelpers';
import { defaultSortFunction } from '../../utils/sortFunctions';
import {
tooltipBaseMean,
Expand Down Expand Up @@ -202,6 +211,131 @@ export const mannWhitneyStrategy = {
};
},

renderSubtestColumns(result: CombinedResultsItemType, expanded: boolean) {
const {
test,
cliffs_delta,
mann_whitney_test,
cles,
direction_of_change,
base_measurement_unit: baseUnit,
new_measurement_unit: newUnit,
base_app: baseApp,
new_app: newApp,
} = result as MannWhitneyResultsItem;
const mann_whitney_interpretation = mann_whitney_test?.interpretation
? capitalize(mann_whitney_test.interpretation)
: '-';
const clesVal = ((cles?.cles ?? 0) * 100).toFixed(2);
const baseAvgValue =
(result as MannWhitneyResultsItem).base_standard_stats?.mean ?? 0;
const newAvgValue =
(result as MannWhitneyResultsItem).new_standard_stats?.mean ?? 0;
return (
<>
<div title={test} className='subtests subtests-mannwhitney' role='cell'>
{test}
</div>
<div className='mann-witney-browser-name cell' role='cell'>
{formatNumber(baseAvgValue)} {baseUnit}
{getBrowserDisplay(baseApp, newApp, expanded) && (
<span className={FontSize.xSmall}>({baseApp})</span>
)}
</div>
<div className='comparison-sign cell' role='cell'>
{determineSign(baseAvgValue, newAvgValue)}
</div>
<div className='mann-witney-browser-name cell' role='cell'>
{formatNumber(newAvgValue)} {newUnit}
{getBrowserDisplay(baseApp, newApp, expanded) && (
<span className={FontSize.xSmall}>({newApp})</span>
)}
</div>
<div className='status cell' role='cell'>
<Box
sx={{
bgcolor:
direction_of_change === 'improvement'
? 'status.improvement'
: direction_of_change === 'regression'
? 'status.regression'
: 'none',
}}
className={`status-hint ${determineStatusHintClass(
direction_of_change === 'improvement',
direction_of_change === 'regression',
)}`}
>
{direction_of_change === 'improvement' ? (
<ThumbUpIcon color='success' />
) : null}
{direction_of_change === 'regression' ? (
<ThumbDownIcon color='error' />
) : null}
{capitalize(direction_of_change ?? '')}
</Box>
</div>
<div className='delta cell' role='cell'>
{' '}
{cliffs_delta || '-'}
</div>
<div className='significance cell' role='cell'>
{mann_whitney_interpretation}
</div>
<div className='effects cell' role='cell'>
{clesVal ? `${clesVal}% ` : '-'}
</div>
</>
);
},

renderExpandedLeft() {
return null;
},

getComparisonResult(result: CombinedResultsItemType) {
return capitalize(
(result as MannWhitneyResultsItem).direction_of_change ?? '',
);
},

renderExpandedRight(result: CombinedResultsItemType) {
const mwResult = result as MannWhitneyResultsItem;
const { cles, cles_direction } = mwResult.cles ?? {
cles: '',
cles_direction: '',
};
const { cliffs_delta, cliffs_interpretation } = mwResult;
const pValue = mwResult.mann_whitney_test?.pvalue;
const p_value_cles = mwResult.mann_whitney_test?.interpretation
? capitalize(mwResult.mann_whitney_test.interpretation)
: '';

return (
<>
<PValCliffsDeltaComp
cliffs_delta={cliffs_delta}
cliffs_interpretation={cliffs_interpretation}
pValue={pValue}
p_value_cles={p_value_cles}
cles={cles}
cles_direction={cles_direction}
/>
<ModeInterpretation result={mwResult} />
</>
);
},

renderExpandedBottom(result: CombinedResultsItemType) {
const mwResult = result as MannWhitneyResultsItem;
return (
<div style={{ display: 'flex' }}>
<MannWhitneyCompareMetrics result={mwResult} />
<StatisticsWarnings result={mwResult} />
</div>
);
},

renderColumns(result: CombinedResultsItemType) {
const { cliffs_delta, direction_of_change, mann_whitney_test, cles } =
result as MannWhitneyResultsItem;
Expand Down
Loading