mirror of
https://github.com/luxfi/safe-wallet.git
synced 2026-10-30 15:19:21 +00:00
* feat: add automated Storybook snapshot testing with CI integration This PR introduces automated Jest snapshot testing for all Storybook stories using the Portable Stories API. ## Changes - **Test Setup**: Created automated test file that discovers and generates JSON snapshots for all `.stories.tsx` files - **CI Pipeline**: Added GitHub Actions workflow to run snapshot tests on PRs and main branch pushes - **Scripts**: Added `test:storybook` and `test:storybook:ci` npm scripts - **Documentation**: Created comprehensive guide in `STORYBOOK_SNAPSHOTS.md` ## Benefits - Automatically catches unintended UI changes in components - Provides reviewable diffs in pull requests - Fast execution compared to visual regression tests - Version-controlled snapshot history - Zero configuration required - automatically discovers all stories ## Usage ```bash yarn test:storybook # Run tests locally yarn test:storybook -u # Update snapshots ``` The CI workflow runs automatically on changes to `apps/web/**` and will fail if snapshots don't match, uploading diffs as artifacts for review. * fix: move docs to docs folder and add workflow permissions - Move STORYBOOK_SNAPSHOTS.md to docs/storybook-snapshots.md - Add permissions block to workflow for security (contents: read) - Fixes CodeQL security warning about GITHUB_TOKEN permissions * fix: use globSync for Jest compatibility - Change from glob.sync to globSync import - Fixes ES module import error in Jest * fix: address CI issues for storybook snapshot tests - Add @storybook and storybook to Jest transformIgnorePatterns for ESM compatibility - Fix TypeScript typing for Story component in tests - Add baseline snapshots for CI to pass - Merged latest dev to get react-dom path fixes in jest config * style: fix prettier formatting in storybook test * fix: exclude storybook tests from main test run Storybook snapshot tests have their own CI workflow (web-storybook-tests.yml), so they don't need to run as part of the main test:ci. This also helps avoid the jest-coverage-report-action character limit issue. * fix: override testPathIgnorePatterns for storybook tests The storybook test script needs to override the testPathIgnorePatterns from jest.config.cjs to allow running the storybook.test.tsx file. * fix: add timeout and continue-on-error to storybook tests The storybook snapshot tests are taking too long and occasionally hanging. Adding a 15-minute timeout and continue-on-error to prevent blocking PRs while the tests are being stabilized. * refactor: use colocated snapshot tests instead of single giant file - Generate individual test files (*.stories.test.tsx) next to each story - Each component gets its own __snapshots__ directory for easier review - Add generator script: yarn generate:storybook-tests - Add storybook-setup.ts to apply global decorators (ThemeProvider, etc.) - Fix workflow: use step outcome for artifact upload condition - Skip NativeSwapsCard story (requires NextRouter mock) This makes snapshot diffs much easier to review since changes are scoped to individual components. --------- Co-authored-by: Hanzo Dev <[email protected]>
72 lines
2.3 KiB
JavaScript
72 lines
2.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Generates individual snapshot test files for each Storybook story file.
|
|
* This creates colocated test files (*.stories.test.tsx) next to each story file,
|
|
* resulting in separate snapshot files per component.
|
|
*
|
|
* Usage: node scripts/generate-storybook-tests.cjs
|
|
*/
|
|
|
|
const { globSync } = require('glob')
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
const SRC_DIR = path.join(__dirname, '../src')
|
|
|
|
// Find all story files
|
|
const storyFiles = globSync(path.join(SRC_DIR, '**/*.stories.tsx'))
|
|
|
|
const testTemplate = (storyImportPath, setupImportPath) => `/**
|
|
* Auto-generated snapshot tests for Storybook stories
|
|
* Run "yarn generate:storybook-tests" to regenerate
|
|
*/
|
|
import '${setupImportPath}'
|
|
import { composeStories } from '@storybook/react'
|
|
import { render } from '@testing-library/react'
|
|
import type { ComponentType } from 'react'
|
|
|
|
import * as stories from '${storyImportPath}'
|
|
|
|
const composedStories = composeStories(stories)
|
|
|
|
describe('${storyImportPath}', () => {
|
|
Object.entries(composedStories).forEach(([storyName, Story]) => {
|
|
test(storyName, () => {
|
|
const StoryComponent = Story as ComponentType
|
|
const { container } = render(<StoryComponent />)
|
|
expect(container.firstChild).toMatchSnapshot()
|
|
})
|
|
})
|
|
})
|
|
`
|
|
|
|
let generated = 0
|
|
let skipped = 0
|
|
|
|
const SETUP_FILE = path.join(SRC_DIR, 'tests/storybook-setup')
|
|
|
|
storyFiles.forEach((storyFilePath) => {
|
|
const testFilePath = storyFilePath.replace('.stories.tsx', '.stories.test.tsx')
|
|
const testFileDir = path.dirname(testFilePath)
|
|
const storyImportPath = './' + path.basename(storyFilePath).replace('.tsx', '')
|
|
|
|
// Calculate relative path from test file to setup file
|
|
const setupImportPath = path.relative(testFileDir, SETUP_FILE).replace(/\\/g, '/')
|
|
|
|
// Skip if test file already exists and wasn't auto-generated
|
|
if (fs.existsSync(testFilePath)) {
|
|
const content = fs.readFileSync(testFilePath, 'utf-8')
|
|
if (!content.includes('Auto-generated snapshot tests')) {
|
|
console.log(`Skipping ${path.relative(SRC_DIR, testFilePath)} (manually created)`)
|
|
skipped++
|
|
return
|
|
}
|
|
}
|
|
|
|
fs.writeFileSync(testFilePath, testTemplate(storyImportPath, setupImportPath))
|
|
console.log(`Generated ${path.relative(SRC_DIR, testFilePath)}`)
|
|
generated++
|
|
})
|
|
|
|
console.log(`\nDone! Generated ${generated} test files, skipped ${skipped}`)
|