Skip to content
Merged
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
d6e2218
Add stress-tests.yml
hectahertz Apr 11, 2025
9544472
Add default stress test
hectahertz Apr 11, 2025
26131f7
Run on push to test
hectahertz Apr 11, 2025
50f396b
Remove shard
hectahertz Apr 11, 2025
b295934
Don't update shapshots
hectahertz Apr 11, 2025
da50be3
Test code
hectahertz Apr 11, 2025
d951fed
Export results
hectahertz Apr 11, 2025
19e85f9
Snapshot
hectahertz Apr 11, 2025
bae25dd
Upload snapshot
hectahertz Apr 11, 2025
e84fedb
--update-snapshots
hectahertz Apr 11, 2025
ffb257a
Warn
hectahertz Apr 11, 2025
9161405
Don't run on load
hectahertz Apr 11, 2025
02b9980
Better button
hectahertz Apr 11, 2025
08ae3d1
Run iterations
hectahertz Apr 11, 2025
3344db6
Framework
hectahertz Apr 11, 2025
a280f61
Extract StressTest
hectahertz Apr 11, 2025
a89d11c
Extract StressTest to utils
hectahertz Apr 11, 2025
4b56f2c
Styling
hectahertz Apr 11, 2025
3db0c29
Basic UI
hectahertz Apr 11, 2025
82691a5
Simplify stress test
hectahertz Apr 11, 2025
180f299
Add ActionList stress test
hectahertz Apr 11, 2025
471af66
Add title to tests
hectahertz Apr 11, 2025
908fd1d
Don't wait for button
hectahertz Apr 11, 2025
5acba58
Fix console.warn
hectahertz Apr 12, 2025
5dc98e6
Group stresstest stories
hectahertz Apr 12, 2025
cb5fcf8
Custom reporter
hectahertz Apr 12, 2025
4d7b2ea
Try attachments
hectahertz Apr 12, 2025
0efe062
Report attachments
hectahertz Apr 12, 2025
69d0f02
Fix reporter
hectahertz Apr 12, 2025
52527ca
Cleanup
hectahertz Apr 12, 2025
a8751e9
Parse attachment
hectahertz Apr 12, 2025
484273f
Fix typo
hectahertz Apr 12, 2025
5b0e1b5
Save results to a file
hectahertz Apr 12, 2025
c181ee3
Fix typo
hectahertz Apr 12, 2025
6e6a85c
Import instead of require
hectahertz Apr 12, 2025
11da535
Test benchmark-action/github-action-benchmark@v1
hectahertz Apr 12, 2025
96e24d0
Accumulate all results
hectahertz Apr 12, 2025
7d26cf7
Tool is customSmallerIsBetter
hectahertz Apr 12, 2025
3275be0
Make it slow to trigger it
hectahertz Apr 12, 2025
54c1186
Add some TODO:
hectahertz Apr 12, 2025
5f83b4b
Add some more TODOs
hectahertz Apr 12, 2025
7db4b07
Run on 2 cores
hectahertz Apr 13, 2025
ab269c0
Clean commits
hectahertz Apr 13, 2025
3611730
Alert commit content
hectahertz Apr 13, 2025
848f82c
Run on 4 cores
hectahertz Apr 13, 2025
f4f38b0
Restore stress test
hectahertz Apr 13, 2025
ac03717
Clean comments
hectahertz Apr 13, 2025
bc01de7
Add comment
hectahertz Apr 13, 2025
031a342
Restyling
hectahertz Apr 13, 2025
96520ec
Clean up StressTest component
hectahertz Apr 13, 2025
0c5fbc8
Display more metrics
hectahertz Apr 13, 2025
04944be
Count elapsed time
hectahertz Apr 13, 2025
14f53f3
Restyle
hectahertz Apr 13, 2025
9fc0e97
Styling
hectahertz Apr 13, 2025
88d2186
Cleanup work
hectahertz Apr 13, 2025
4c00bff
Add a couple of Button stress tests
hectahertz Apr 13, 2025
0f2863d
Change Icon
hectahertz Apr 13, 2025
09a9e6e
Reduce iterations on ActionList test
hectahertz Apr 13, 2025
8c4979e
Update TODOs
hectahertz Apr 13, 2025
5f0fd69
Reduce actionlist Iterations
hectahertz Apr 13, 2025
85f8603
Styling
hectahertz Apr 15, 2025
a543f56
Add TreeView stress test
hectahertz Apr 15, 2025
0ebe91c
Cleanup reporter
hectahertz Apr 15, 2025
932e4eb
Lower CurrentUpdate iterations
hectahertz Apr 15, 2025
c109f18
Rename reporter
hectahertz Apr 15, 2025
220cd24
Remove TODOs
hectahertz Apr 15, 2025
120f667
Unify all stress tests in one file
hectahertz Apr 15, 2025
c763ffc
Clean up
hectahertz Apr 15, 2025
dac0e50
Merge branch 'main' into hectahertz/stress-test
hectahertz Apr 15, 2025
f330e44
Add changeset
hectahertz Apr 15, 2025
28a2d89
Merge branch 'main' into hectahertz/stress-test
hectahertz Apr 18, 2025
443736a
Fix lint errors
hectahertz Apr 18, 2025
ee8f6de
Merge branch 'main' into hectahertz/stress-test
hectahertz Apr 18, 2025
1d9892f
Fix css linting issues
hectahertz Apr 18, 2025
2b9aa23
Make the stress stories dev only
hectahertz Apr 19, 2025
0c5a18a
Merge branch 'main' into hectahertz/stress-test
jonrohan Apr 22, 2025
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
5 changes: 5 additions & 0 deletions .changeset/eleven-crabs-achieved.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

Add the framework required to run component stress tests
55 changes: 55 additions & 0 deletions .github/workflows/stress-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Stress Tests

on: [push]

jobs:
stress-tests:
runs-on: ubuntu-latest-4-cores
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Set up Node.js
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build storybook
run: npx storybook build
working-directory: packages/react
- name: Run storybook
id: storybook
run: |
npx serve -l 6006 packages/react/storybook-static &
pid=$!
echo "pid=$pid" >> $GITHUB_OUTPUT
sleep 5
- name: Run Stress Tests
uses: docker://mcr.microsoft.com/playwright:v1.51.0-jammy
env:
STORYBOOK_URL: 'http://172.17.0.1:6006'
with:
args: npx playwright test --grep @stress-test"
- name: Stop storybook
if: ${{ always() }}
run: kill ${{ steps.storybook.outputs.pid }}
- name: Download previous benchmark data (if any)
uses: actions/cache@v4
with:
path: ./cache
key: stress-tests-benchmark
- name: Store benchmark result
uses: benchmark-action/github-action-benchmark@v1
with:
tool: 'customSmallerIsBetter'
output-file-path: results.json
# Where the previous data file is stored
external-data-json-path: ./cache/stress-tests-benchmark-data.json
# Workflow will fail when an alert happens
fail-on-alert: true
github-token: ${{ secrets.GITHUB_TOKEN }}
alert-threshold: '150%'
# Enable alert commit comment
comment-on-alert: true
# Mention @rhysd in the commit comment
alert-comment-cc-users: '@hectahertz'
24 changes: 24 additions & 0 deletions e2e/components/StressTests.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {test} from '@playwright/test'
import {visit} from '../test-helpers/storybook'

const stressTests = [
{component: 'ActionList', testName: 'Single select', id: 'stresstests-components-actionlist--single-select'},
{component: 'Pagination', testName: 'Page update', id: 'stresstests-components-pagination--page-update'},
{component: 'Button', testName: 'Label update', id: 'stresstests-components-button--label-update'},
{component: 'Button', testName: 'Count update', id: 'stresstests-components-button--count-update'},
{component: 'TreeView', testName: 'Current update', id: 'stresstests-components-treeview--current-update'},
]

stressTests.forEach(({component, testName, id}) => {

Check failure on line 12 in e2e/components/StressTests.test.ts

View workflow job for this annotation

GitHub Actions / lint

Prefer for...of instead of Array.forEach
test.describe(`${component} Stress Tests`, () => {
test(`${testName} @stress-test`, async ({page}, testInfo) => {
await visit(page, {id})
await page.getByTestId('start').click()
const result = await page.getByTestId('result').textContent()
await testInfo.attach('stress-test-result', {
body: JSON.stringify({id, duration: result}),
contentType: 'application/json',
})
})
})
})
39 changes: 39 additions & 0 deletions e2e/stress-test-reporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type {FullResult, Reporter, TestCase, TestResult} from '@playwright/test/reporter'
import {writeFileSync} from 'fs'

class MyReporter implements Reporter {
// Format we need:
// https://github.com/benchmark-action/github-action-benchmark?tab=readme-ov-file#examples
results: {name: string; unit: string; value: number}[] = []

onTestEnd(test: TestCase, result: TestResult) {
console.log(`⚡️ Finished stress-test ${test.title}: ${result.status}`)

Check failure on line 10 in e2e/stress-test-reporter.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
for (const attachment of result.attachments) {
// get the content of the attachment to an object
if (
attachment.body !== undefined &&
attachment.name === 'stress-test-result' &&
attachment.contentType === 'application/json'
) {
const content = JSON.parse(attachment.body.toString())
console.log(`⚡️ Test result: ${content}`)

Check failure on line 19 in e2e/stress-test-reporter.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
this.results.push({
name: content.id,
unit: 'ms',
value: parseFloat(content.duration),
})
}
}
}

onEnd(result: FullResult) {
console.log(`⚡️ Finished the stress tests run: ${result.status}`)

Check failure on line 30 in e2e/stress-test-reporter.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
const fileName = 'results.json'
const fileContentString = JSON.stringify(this.results, null, 2)
console.log(`⚡️ Results file content: ${fileContentString}`)

Check failure on line 33 in e2e/stress-test-reporter.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
writeFileSync(fileName, fileContentString)
console.log(`⚡️ File saved: ${fileName}`)

Check failure on line 35 in e2e/stress-test-reporter.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
}
}

export default MyReporter
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@
"@types/semver": "7.7.0",
"@types/styled-components": "^5.1.26",
"@vitejs/plugin-react": "^4.3.3",
"afterframe": "^1.0.2",
"ajv": "8.16.0",
"axe-core": "4.9.1",
"babel-core": "7.0.0-bridge.0",
Expand Down
52 changes: 52 additions & 0 deletions packages/react/src/ActionList/ActionList.stress.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react'

import type {Meta} from '@storybook/react'
import type {ComponentProps} from '../utils/types'
import {StressTest} from '../utils/StressTest'
import {TableIcon} from '@primer/octicons-react'
import {ActionList} from '.'

export default {
title: 'StressTests/Components/ActionList',
component: ActionList,
} as Meta<ComponentProps<typeof ActionList>>

const totalIterations = 100

const projects = Array.from({length: totalIterations}, (_, i) => ({
name: `Project ${i + 1}`,
scope: `Scope ${i + 1}`,
}))

export const SingleSelect = () => {
return (
<StressTest
componentName="ActionList"
title="Single Select"
description="Selecting a single item from a large list."
totalIterations={totalIterations}
renderIteration={count => {
return (
<>
<ActionList selectionVariant="single" showDividers role="menu" aria-label="Project">
{projects.map((project, index) => (
<ActionList.Item
key={index}
role="menuitemradio"
selected={index === count}
aria-checked={index === count}
>
<ActionList.LeadingVisual>
<TableIcon />
</ActionList.LeadingVisual>
{project.name}
<ActionList.Description variant="block">{project.scope}</ActionList.Description>
</ActionList.Item>
))}
</ActionList>
</>
)
}}
/>
)
}
61 changes: 61 additions & 0 deletions packages/react/src/Button/Button.stress.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react'

import type {Meta} from '@storybook/react'
import type {ComponentProps} from '../utils/types'
import {StressTest} from '../utils/StressTest'
import {Button} from '.'

export default {
title: 'StressTests/Components/Button',
component: Button,
} as Meta<ComponentProps<typeof Button>>

const totalIterations = 500

export const LabelUpdate = () => {
return (
<StressTest
componentName="Button"
title="Label update"
description="Update the label a large number of times."
totalIterations={totalIterations}
renderIteration={count => (
<div>
<Button variant="primary" size="large" onClick={() => {}}>
{`Button ${count + 1}`}
</Button>
<Button variant="default" size="medium" onClick={() => {}}>
{`Button ${count + 1}`}
</Button>
<Button variant="danger" size="small" onClick={() => {}}>
{`Button ${count + 1}`}
</Button>
</div>
)}
/>
)
}

export const CountUpdate = () => {
return (
<StressTest
componentName="Button"
title="Count update"
description="Update the count a large number of times."
totalIterations={totalIterations}
renderIteration={count => (
<div>
<Button variant="primary" size="large" onClick={() => {}} count={count}>
Button
</Button>
<Button variant="default" size="medium" onClick={() => {}} count={count}>
Button
</Button>
<Button variant="danger" size="small" onClick={() => {}} count={count}>
Button
</Button>
</div>
)}
/>
)
}
27 changes: 27 additions & 0 deletions packages/react/src/Pagination/Pagination.stress.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'

import type {Meta} from '@storybook/react'
import type {ComponentProps} from '../utils/types'
import Pagination from './Pagination'
import {StressTest} from '../utils/StressTest'

export default {
title: 'StressTests/Components/Pagination',
component: Pagination,
} as Meta<ComponentProps<typeof Pagination>>

const totalIterations = 500

export const PageUpdate = () => {
return (
<StressTest
componentName="Pagination"
title="Page update"
description="Navigation through a large number of pages."
totalIterations={totalIterations}
renderIteration={count => (
<Pagination pageCount={totalIterations} currentPage={count + 1} showPages={{narrow: false}} />
)}
/>
)
}
53 changes: 53 additions & 0 deletions packages/react/src/TreeView/TreeView.stress.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react'

import type {Meta} from '@storybook/react'
import type {ComponentProps} from '../utils/types'
import {StressTest} from '../utils/StressTest'
import {TreeView} from './TreeView'
import {FileIcon, DiffAddedIcon, DiffModifiedIcon} from '@primer/octicons-react'

Check failure on line 7 in packages/react/src/TreeView/TreeView.stress.stories.tsx

View workflow job for this annotation

GitHub Actions / lint

'DiffModifiedIcon' is defined but never used
import Octicon from '../Octicon'

export default {
title: 'StressTests/Components/TreeView',
component: TreeView,
} as Meta<ComponentProps<typeof TreeView>>

const totalIterations = 100

const Files = Array.from({length: totalIterations}, (_, i) => ({
name: `File_${i + 1}.tsx`,
}))

export const CurrentUpdate = () => {
return (
<StressTest
componentName="TreeView"
title="Simple current update"
description="Marking a file as current from a large list."
totalIterations={totalIterations}
renderIteration={count => (
<TreeView aria-label="Files changed">
<TreeView.Item id="src" defaultExpanded>
<TreeView.LeadingVisual>
<TreeView.DirectoryIcon />
</TreeView.LeadingVisual>
src
<TreeView.SubTree>
{Files.map((file, index) => (
<TreeView.Item key={index} id={`src/${file.name}`} current={index === count}>
<TreeView.LeadingVisual>
<FileIcon />
</TreeView.LeadingVisual>
{file.name}
<TreeView.TrailingVisual label="Added">
<Octicon icon={DiffAddedIcon} color="success.fg" />
</TreeView.TrailingVisual>
</TreeView.Item>
))}
</TreeView.SubTree>
</TreeView.Item>
</TreeView>
)}
/>
)
}
Loading
Loading