Skip to content
Closed
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
26 changes: 24 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for Chromatic

- name: Setup Node.js
uses: actions/setup-node@v4
Expand All @@ -29,14 +31,34 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Install Playwright Browsers
run: npx playwright install --with-deps

- name: Lint code
run: npm run lint

- name: Build
run: npm run build

- name: Run tests
run: npm test
- name: Run Unit & Component Tests
run: npm test -- --coverage

- name: Check Coverage Thresholds
run: |
# Jest will exit with non-zero if thresholds are not met
npm test -- --coverage --coverageThreshold='{"global":{"branches":85,"functions":85,"lines":85,"statements":85}}'

- name: Run E2E Tests
run: npx playwright test

- name: Run Lighthouse CI
run: npx lhci autoreview
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

- name: Run Chromatic
run: npx chromatic --project-token=${{ secrets.CHROMATIC_PROJECT_TOKEN }}
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

# Server (TypeScript) CI
server:
Expand Down
4 changes: 4 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
PULL_REQUEST.md
# dependencies
/node_modules
/.tools
/.pnp
.pnp.js

# testing
/coverage
/playwright-report
/test-results
/.lighthouseci

# next.js
/.next/
Expand Down
21 changes: 21 additions & 0 deletions frontend/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { StorybookConfig } from "@storybook/nextjs";

const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
"@storybook/addon-a11y",
],
framework: {
name: "@storybook/nextjs",
options: {},
},
docs: {
autodocs: "tag",
},
staticDirs: ["../public"],
};
export default config;
16 changes: 16 additions & 0 deletions frontend/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Preview } from "@storybook/react";
import "../src/styles/globals.css"; // Ensure this path is correct

const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};

export default preview;
15 changes: 15 additions & 0 deletions frontend/e2e/basic.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { test, expect } from '@playwright/test';
import { injectAxe, checkA11y } from 'axe-playwright';

test.describe('Basic Navigation', () => {
test('should navigate to the home page', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/ArenaX/);
});

test('should have no accessibility violations', async ({ page }) => {
await page.goto('/');
await injectAxe(page);
await checkA11y(page);
});
});
23 changes: 18 additions & 5 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
/** @type {import('jest').Config} */
module.exports = {
const nextJest = require("next/jest");

const createJestConfig = nextJest({
dir: "./",
});

const config = {
setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
testEnvironment: "jest-environment-jsdom",
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
"\\.(css|less|scss|sass)$": "<rootDir>/__mocks__/styleMock.js",
"\\.(jpg|jpeg|png|gif|svg|webp)$": "<rootDir>/__mocks__/fileMock.js",
},
transform: {
"^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { configFile: "./babel.config.js" }],
collectCoverage: true,
coverageThreshold: {
global: {
branches: 85,
functions: 85,
lines: 85,
statements: 85,
},
},
coverageReporters: ["json", "lcov", "text", "clover", "json-summary"],
};

module.exports = createJestConfig(config);
20 changes: 20 additions & 0 deletions frontend/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import "@testing-library/jest-dom";
import { toHaveNoViolations } from "jest-axe";
import { TextDecoder, TextEncoder } from "util";

expect.extend(toHaveNoViolations);

if (typeof global.TextEncoder === "undefined") {
// Required by stellar-sdk in Jest runtime.
global.TextEncoder = TextEncoder as unknown as typeof global.TextEncoder;
Expand All @@ -9,3 +12,20 @@ if (typeof global.TextEncoder === "undefined") {
if (typeof global.TextDecoder === "undefined") {
global.TextDecoder = TextDecoder as unknown as typeof global.TextDecoder;
}

// Mock window.matchMedia
if (typeof window !== 'undefined') {
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
}
20 changes: 20 additions & 0 deletions frontend/lighthouserc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
ci: {
collect: {
startServerCommand: 'npm run start',
url: ['http://localhost:3000'],
numberOfRuns: 3,
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.85 }],
'categories:accessibility': ['error', { minScore: 0.9 }],
'categories:best-practices': ['error', { minScore: 0.9 }],
'categories:seo': ['error', { minScore: 0.9 }],
},
},
upload: {
target: 'temporary-public-storage',
},
},
};
Loading
Loading