Skip to content
Open
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
51 changes: 51 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
module.exports = {
root: true,
extends: ["next/core-web-vitals", "next/typescript"],
ignorePatterns: [
"node_modules/",
".next/",
"out/",
"refs/",
"subgraph/",
"scripts/",
"*.config.js",
"*.config.cjs",
"*.config.ts",
"postcss.config.cjs",
"next-env.d.ts",
],
rules: {
"no-console": ["warn", { allow: ["warn", "error"] }],
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
"react-hooks/rules-of-hooks": "warn",
"react-hooks/exhaustive-deps": "warn",
"react/no-unescaped-entities": "off",
"@typescript-eslint/triple-slash-reference": "off",
"@next/next/no-html-link-for-pages": "warn",
"react/jsx-key": "warn",
"prefer-const": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/prefer-nullish-coalescing": "off",
"import/order": "off",
},
overrides: [
{
files: ["app/api/**/*.ts"],
rules: {
"react-hooks/rules-of-hooks": "off",
"react-hooks/exhaustive-deps": "off",
},
},
{
files: ["lib/**/*.ts", "lib/**/*.tsx"],
rules: {
"no-console": "off",
},
},
],
};

32 changes: 32 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## What

<!-- Brief description of changes -->

## Why

<!-- Motivation or linked issue -->
<!-- Reference: ENGINEERING_GUIDELINES.md - explain why, not what -->

## Testing

<!-- What was tested manually and/or by CI -->

- [ ] Build passes (`pnpm build`)
- [ ] Unit tests pass (`pnpm test`)
- [ ] E2E tests pass (`pnpm e2e`) - if applicable
- [ ] Manual testing completed for affected features

## Checklist

- [ ] Build passes (`pnpm build` succeeds) ⚠️ MANDATORY
- [ ] No secrets in code, commit messages, or documentation
- [ ] No hardcoded credentials as fallbacks
- [ ] Documentation updated if needed (public docs in `docs/`, not `refs/`)
- [ ] Code is clean (no commented code, unused imports, debug logs)
- [ ] Commit message is clear and doesn't mention secrets
- [ ] Wallet addresses normalized in Arkiv operations (if applicable)
- [ ] Error handling is graceful and defensive

## Notes

<!-- Any additional context, breaking changes, or follow-up work needed -->
28 changes: 28 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Dependabot configuration for automated dependency updates
#
# Checks npm dependencies weekly and auto-opens PRs for security updates.
# Helps maintain security posture without manual monitoring.

version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
# Open PRs for security updates immediately
open-pull-requests-limit: 10
# Group updates to reduce PR noise
groups:
security-updates:
dependency-type: "development"
update-types:
- "security"
# Labels for PRs
labels:
- "dependencies"
- "automated"
# Commit message format
commit-message:
prefix: "chore"
include: "scope"

161 changes: 161 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# CI Pipeline - Runs on every push and PR
# This ensures code quality before it reaches main

name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

# Cancel in-progress runs when a new commit is pushed
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
quality:
name: Code Quality
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8.6.0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile
env:
HUSKY: "0" # Disable husky in CI (not needed, hooks run locally)

- name: Check formatting (Prettier)
run: pnpm format:check

- name: Lint (ESLint)
run: pnpm lint

- name: Type check (TypeScript)
run: pnpm typecheck

- name: Run tests
run: pnpm test

- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
retention-days: 7

build:
name: Build
runs-on: ubuntu-latest
needs: quality # Only build if quality checks pass

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8.6.0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile
env:
HUSKY: "0" # Disable husky in CI (not needed, hooks run locally)

- name: Build application
run: pnpm build

e2e:
name: E2E Tests
runs-on: ubuntu-latest
needs: build # Run E2E after build succeeds
continue-on-error: true # Allow failures initially until tests are stable

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8.6.0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile
env:
HUSKY: "0" # Disable husky in CI (not needed, hooks run locally)

- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium

- name: Build application
run: pnpm build

- name: Start application
run: pnpm start &
env:
NEXT_PUBLIC_E2E_MOCKS: "true"

- name: Wait for server
run: |
timeout=60
counter=0
until curl -f http://localhost:3000 > /dev/null 2>&1; do
if [ $counter -ge $timeout ]; then
echo "Server failed to start within $timeout seconds"
exit 1
fi
echo "Waiting for server... ($counter/$timeout)"
sleep 2
counter=$((counter + 2))
done
echo "Server is ready!"

- name: Run E2E smoke tests (mocked)
run: pnpm e2e e2e/smoke.spec.ts
env:
PLAYWRIGHT_BASE_URL: "http://localhost:3000"
NEXT_PUBLIC_E2E_MOCKS: "true"

- name: Run E2E real Arkiv tests (real services)
run: pnpm e2e e2e/arkiv-real.spec.ts
env:
PLAYWRIGHT_BASE_URL: "http://localhost:3000"
NEXT_PUBLIC_E2E_MOCKS: "false"
continue-on-error: true # May fail if Arkiv/RPC unavailable - that's OK

- name: Upload test results
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 30
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pnpm lint-staged
9 changes: 9 additions & 0 deletions .lintstagedrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"*.{ts,tsx,js,jsx}": [
"eslint --fix --max-warnings 1000",
"prettier --write"
],
"*.{json,md,css}": [
"prettier --write"
]
}
35 changes: 35 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Dependencies
node_modules/

# Build outputs
.next/
out/

# Internal references
refs/

# Generated / lockfiles
pnpm-lock.yaml

# Subgraph (separate tooling)
subgraph/
# Dependencies
node_modules/

# Build outputs
.next/
out/

# Reference files (not our code)
refs/

# Subgraph (has its own tooling)
subgraph/

# Lock files
pnpm-lock.yaml

# Generated files
*.min.js
*.min.css

8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"plugins": ["prettier-plugin-tailwindcss"]
}
55 changes: 55 additions & 0 deletions __tests__/example.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/// <reference types="vitest" />

describe("Example Test Suite", () => {
it("adds numbers", () => {
expect(1 + 1).toBe(2);
});

it("checks strings", () => {
expect("p2pmentor").toContain("mentor");
});
});
// Example test file - shows how to write tests
// Put your tests in __tests__/ or name them *.test.ts

import { describe, it, expect } from "vitest";

describe("Example Test Suite", () => {
it("should pass a basic assertion", () => {
expect(1 + 1).toBe(2);
});

it("should work with strings", () => {
const greeting = "Hello, p2pmentor!";
expect(greeting).toContain("p2pmentor");
});

it("should work with arrays", () => {
const skills = ["TypeScript", "React", "Solidity"];
expect(skills).toHaveLength(3);
expect(skills).toContain("React");
});

it("should work with objects", () => {
const profile = {
wallet: "0x1234...",
displayName: "Alice",
skills: ["GraphQL", "Next.js"],
};

expect(profile).toHaveProperty("wallet");
expect(profile.skills).toContain("GraphQL");
});
});

// Example: Testing a utility function
// Uncomment and adapt when you have utils to test

// import { formatWalletAddress } from "@/lib/utils";
//
// describe("formatWalletAddress", () => {
// it("should truncate long addresses", () => {
// const full = "0x1234567890abcdef1234567890abcdef12345678";
// expect(formatWalletAddress(full)).toBe("0x1234...5678");
// });
// });
Loading
Loading