mirror of
https://github.com/luxfi/safe-wallet.git
synced 2026-10-30 15:19:21 +00:00
* chore: integrate Chromatic E2E visual testing for Cypress smoke tests
Replace unused cypress-visual-regression plugin with @chromatic-com/cypress
for cloud-based visual diffing of E2E tests.
Changes:
- Add @chromatic-com/cypress, remove cypress-visual-regression
- Update Cypress config and support files for Chromatic plugin
- Clean up obsolete visual regression test and snapshots
- Add Chromatic archive generation to CI workflow
- Configure artifact upload and Chromatic job in smoke test workflow
- Add .chromatic to .gitignore
- Add cypress.config.js to ESLint ignores
The chromatic job runs after successful E2E tests, downloads archives from
parallel containers, and uploads to Chromatic for visual regression analysis.
Requires CHROMATIC_E2E_PROJECT_TOKEN secret to be configured.
* chore: fix Chromatic E2E archive paths and upload method
- Fix artifact upload/download paths from .chromatic to cypress/downloads
- Replace non-existent 'npx chromatic --cypress' with chromaui/action
- Add CHROMATIC_ARCHIVE_LOCATION env var for proper archive discovery
- Update .gitignore to ignore cypress/downloads directory
* chore: use correct Chromatic project token for E2E tests
Use CHROMATIC_PROJECT_TOKEN (same as Storybook workflow) instead of
non-existent CHROMATIC_E2E_PROJECT_TOKEN
* chore: add two-scope manual Chromatic E2E workflow
- Add workflow_dispatch input with 'smoke' (default) and 'full' scopes
- Smoke scope: 17 tests across 5 containers (cypress/e2e/smoke/*.cy.js)
- Full scope: 100+ tests across 10 containers (smoke + regression + safe-apps)
- Remove Chromatic integration from regular smoke test pipeline
- Chromatic E2E now exclusively in manual workflow
* chore: add explicit permissions to E2E workflows
- Add `permissions: { contents: read }` to limit GITHUB_TOKEN scope
- Applied to e2e job in web-e2e-smoke.yml
- Applied to e2e and chromatic jobs in web-chromatic-e2e.yml
- Addresses GitHub Advanced Security code scanning findings
---------
Co-authored-by: Hanzo Dev <[email protected]>
156 lines
5.6 KiB
JavaScript
156 lines
5.6 KiB
JavaScript
import unusedImports from 'eslint-plugin-unused-imports'
|
|
import typescriptEslint from '@typescript-eslint/eslint-plugin'
|
|
import noOnlyTests from 'eslint-plugin-no-only-tests'
|
|
import tsParser from '@typescript-eslint/parser'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import js from '@eslint/js'
|
|
import { FlatCompat } from '@eslint/eslintrc'
|
|
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
const __dirname = path.dirname(__filename)
|
|
const compat = new FlatCompat({
|
|
baseDirectory: __dirname,
|
|
recommendedConfig: js.configs.recommended,
|
|
allConfig: js.configs.all,
|
|
})
|
|
|
|
export default [
|
|
{
|
|
ignores: [
|
|
'**/node_modules/',
|
|
'**/.next/',
|
|
'**/.github/',
|
|
'**/cypress/',
|
|
'**/cypress.config.js',
|
|
'**/src/types/contracts/',
|
|
'**/.storybook/test-runner.mjs',
|
|
'**/.storybook/mocks/*.js',
|
|
'**/public/mockServiceWorker.js',
|
|
],
|
|
},
|
|
...compat.extends('next', 'prettier', 'plugin:storybook/recommended'),
|
|
{
|
|
plugins: {
|
|
'unused-imports': unusedImports,
|
|
'@typescript-eslint': typescriptEslint,
|
|
'no-only-tests': noOnlyTests,
|
|
},
|
|
|
|
languageOptions: {
|
|
parser: tsParser,
|
|
ecmaVersion: 5,
|
|
sourceType: 'script',
|
|
|
|
parserOptions: {
|
|
project: ['./tsconfig.json'],
|
|
},
|
|
},
|
|
|
|
rules: {
|
|
'@next/next/no-img-element': 'off',
|
|
'@next/next/google-font-display': 'off',
|
|
'@next/next/google-font-preconnect': 'off',
|
|
'@next/next/no-page-custom-font': 'off',
|
|
'unused-imports/no-unused-imports': 'error',
|
|
'@typescript-eslint/consistent-type-imports': 'error',
|
|
'@typescript-eslint/await-thenable': 'error',
|
|
'no-constant-condition': 'warn',
|
|
|
|
'unused-imports/no-unused-vars': [
|
|
'error',
|
|
{
|
|
varsIgnorePattern: '^_',
|
|
},
|
|
],
|
|
|
|
'react-hooks/exhaustive-deps': [
|
|
'warn',
|
|
{
|
|
additionalHooks: 'useAsync',
|
|
},
|
|
],
|
|
|
|
'no-only-tests/no-only-tests': 'error',
|
|
'object-shorthand': ['error', 'properties'],
|
|
'jsx-quotes': ['error', 'prefer-double'],
|
|
|
|
'react/jsx-curly-brace-presence': [
|
|
'error',
|
|
{
|
|
props: 'never',
|
|
children: 'never',
|
|
},
|
|
],
|
|
|
|
// Feature architecture: Prevent importing feature internals from outside the feature
|
|
// This enforces that features expose a clean public API through their index.ts barrel file
|
|
//
|
|
// ALLOWED imports:
|
|
// @/features/myfeature - main barrel (components via useLoadFeature, types, lightweight hooks)
|
|
// @/features/myfeature/store - Redux store (slice, selectors, actions) - needed at store init
|
|
// @/features/myfeature/services - services barrel (lightweight utilities only)
|
|
//
|
|
// FORBIDDEN imports (will cause bundle bloat):
|
|
// @/features/myfeature/components/* - use useLoadFeature() instead
|
|
// @/features/myfeature/hooks/* - export through feature barrel if lightweight
|
|
// @/features/myfeature/services/* - heavy services should be in contract, accessed via useLoadFeature()
|
|
//
|
|
// See apps/web/docs/feature-architecture.md for details
|
|
'no-restricted-imports': [
|
|
'warn',
|
|
{
|
|
patterns: [
|
|
// Block deep imports into feature components (defeats lazy loading)
|
|
{
|
|
group: ['@/features/*/components', '@/features/*/components/**'],
|
|
message:
|
|
'Do not import components directly. Use useLoadFeature() to access lazy-loaded components. See docs/feature-architecture.md',
|
|
},
|
|
// Block deep imports into feature hooks (should go through barrel)
|
|
{
|
|
group: ['@/features/*/hooks', '@/features/*/hooks/**'],
|
|
message: 'Import hooks from the feature barrel (@/features/myfeature) not from hooks folder directly.',
|
|
},
|
|
// Block deep imports into services internal files (barrel is OK for lightweight utils)
|
|
{
|
|
group: ['@/features/*/services/*', '!@/features/*/services/index'],
|
|
message:
|
|
'Import from @/features/myfeature/services (barrel) for lightweight utils, or use useLoadFeature() for heavy services.',
|
|
},
|
|
// Block deep imports into store internal files (barrel is OK)
|
|
{
|
|
group: ['@/features/*/store/*', '!@/features/*/store/index'],
|
|
message: 'Import from @/features/myfeature/store (barrel) not from internal store files.',
|
|
},
|
|
// Block internal file imports (handle.ts is internal, only index.ts is public)
|
|
{
|
|
group: ['@/features/*/handle'],
|
|
message: 'Import from feature index file only. The handle is internal - use @/features/{name} instead.',
|
|
},
|
|
// Same patterns for relative imports
|
|
{
|
|
// Same for relative imports
|
|
group: [
|
|
'../features/*/components',
|
|
'../features/*/components/**',
|
|
'../../features/*/components',
|
|
'../../features/*/components/**',
|
|
],
|
|
message: 'Do not import components directly. Use useLoadFeature() instead.',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
// Override for story files: allow type-only imports from @storybook/react
|
|
// since @storybook/nextjs re-exports these types but TypeScript doesn't always resolve them correctly
|
|
{
|
|
files: ['**/*.stories.tsx', '**/*.stories.ts'],
|
|
rules: {
|
|
'storybook/no-renderer-packages': 'off',
|
|
},
|
|
},
|
|
]
|