Contributing Guide
This guide covers the complete setup process for contributing to Multiforum.
Prerequisites
- Node.js 22.x or higher
- npm (included with Node.js)
- Git
- Neo4j (local or cloud instance for testing)
Repository Setup
Clone the Repositories
# Frontend
git clone https://github.com/gennit-project/multiforum-nuxt.git
cd multiforum-nuxt
npm install
# Backend (in a separate directory)
git clone https://github.com/gennit-project/multiforum-backend.git
cd multiforum-backend
npm install
Environment Variables
Frontend (.env)
Copy .env.example to .env and configure:
# Core
GRAPHQL_URL_FOR_TYPES=http://localhost:4000/graphql
VITE_GRAPHQL_URL=http://localhost:4000/graphql
VITE_BASE_URL=http://localhost:3000
VITE_SERVER_NAME=dev-server
VITE_SERVER_DISPLAY_NAME=Development Server
VITE_ENVIRONMENT=development
# Authentication (Auth0)
VITE_AUTH0_DOMAIN=your-tenant.auth0.com
VITE_AUTH0_CLIENT_ID=your-client-id
VITE_AUTH0_CLIENT_SECRET=your-client-secret
VITE_AUTH0_AUDIENCE=your-api-audience
VITE_AUTH0_SCOPE=openid profile email
VITE_AUTH0_URL=https://your-tenant.auth0.com/oauth/token
VITE_AUTH0_CALLBACK_URL=http://localhost:3000/callback
VITE_LOGOUT_URL=http://localhost:3000
# Optional services
VITE_GOOGLE_MAPS_API_KEY=your-api-key
VITE_GOOGLE_CLOUD_STORAGE_BUCKET=your-bucket
Backend (.env)
# Database
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your-password
# Server
PORT=4000
# Authentication
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your-client-id
# Optional
GCS_BUCKET_NAME=your-bucket
GOOGLE_CREDENTIALS_BASE64=base64-encoded-credentials
EMAIL_PROVIDER=resend
RESEND_API_KEY=your-api-key
Running Locally
Start the Backend
cd multiforum-backend
npm run dev
The GraphQL server starts at http://localhost:4000/graphql.
Start the Frontend
cd multiforum-nuxt
npm run dev
The frontend starts at http://localhost:3000.
Testing
Unit Tests (Frontend)
cd multiforum-nuxt
npm run test:unit
Uses Vitest with Vue Test Utils.
E2E Tests (Frontend)
cd multiforum-nuxt
npm run test:playwright
Uses Playwright for end-to-end testing.
Running Specific Tests
# Run a specific test file
npm run test:unit -- src/utils/permissionUtils.spec.ts
# Run tests matching a pattern
npm run test:unit -- -t "permission"
Code Style
TypeScript
- Strict mode enabled
- Explicit types for function parameters and returns
- Use interfaces for object shapes
- Avoid
any; useunknownif needed
// Good
function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Avoid
function calculateTotal(items: any): any {
return items.reduce((sum: any, item: any) => sum + item.price, 0);
}
Vue Components
Use <script setup> with TypeScript:
<script setup lang="ts">
interface Props {
title: string;
count?: number;
}
const props = withDefaults(defineProps<Props>(), {
count: 0,
});
const emit = defineEmits<{
(e: "update", value: number): void;
}>();
</script>
<template>
<div>
<h1>{{ title }}</h1>
<span>{{ count }}</span>
</div>
</template>
Composables
Extract reusable logic into composables:
// composables/useCounter.ts
export function useCounter(initialValue = 0) {
const count = ref(initialValue);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return { count, increment, decrement };
}
Testing Patterns
Unit Tests
import { describe, it, expect } from "vitest";
import { mount } from "@vue/test-utils";
import MyComponent from "@/components/MyComponent.vue";
describe("MyComponent", () => {
it("renders correctly with default props", () => {
const wrapper = mount(MyComponent);
expect(wrapper.exists()).toBe(true);
});
it("emits event when clicked", async () => {
const wrapper = mount(MyComponent);
await wrapper.find("button").trigger("click");
expect(wrapper.emitted()).toHaveProperty("click");
});
});
E2E Tests
import { test, expect } from "@playwright/test";
test("user can create a discussion", async ({ page }) => {
// Navigate
await page.goto("/forums/test-channel");
// Create discussion
await page.click('[data-testid="create-discussion-button"]');
await page.fill('[data-testid="title-input"]', "Test Discussion");
await page.fill('[data-testid="body-input"]', "Test content");
await page.click('[data-testid="save-button"]');
// Verify
await expect(page.locator('[data-testid="discussion-title"]'))
.toContainText("Test Discussion");
});
Test Best Practices
Do:
- Wait for network requests instead of arbitrary timeouts
- Use
data-testidattributes for selectors - Test behavior, not implementation
- Keep tests independent
Don't:
- Use
cy.wait(3000)orpage.waitForTimeout(3000) - Select by CSS classes that may change
- Test internal component state
- Create tests that depend on each other
GraphQL Development
Generating Types
After schema changes:
cd multiforum-nuxt
npm run codegen
This generates TypeScript types from the GraphQL schema.
Adding Queries/Mutations
- Add the GraphQL operation to
graphQLData/:
// graphQLData/discussion/queries.ts
export const GET_DISCUSSION = gql`
query GetDiscussion($id: ID!) {
discussions(where: { id: $id }) {
id
title
body
Author {
username
}
}
}
`;
- Run codegen to generate types
- Use in components with Apollo
Backend Resolvers
Custom resolvers go in customResolvers/:
// customResolvers/mutations/createDiscussion.ts
export const createDiscussion = async (
_root: any,
args: { input: DiscussionInput },
context: Context
) => {
const { ogm, driver } = context;
// Validate input
// Create in database
// Return result
};
Pull Request Checklist
Before submitting:
- Code follows style guidelines
- TypeScript compiles without errors (
npm run tsc) - Unit tests pass (
npm run test:unit) - E2E tests pass (
npm run test:playwright) - Linting passes (
npm run lint) - Documentation updated if needed
- Commit messages are clear
Husky Pre-commit Hooks
The project uses Husky for pre-commit checks:
- TypeScript compilation
- Unit tests
- Linting
If commits fail, fix the issues before committing.
Common Issues
Neo4j Connection Failed
- Ensure Neo4j is running
- Check credentials in
.env - Verify port 7687 is accessible
Auth0 Errors
- Verify callback URLs in Auth0 dashboard
- Check client ID and secret
- Ensure audience matches API identifier
Type Errors After Schema Changes
- Run
npm run codegen - Restart the TypeScript language server
- Check for breaking schema changes
Getting Help
- Check existing documentation
- Search closed issues and PRs
- Open a discussion for questions
- Contact: catherine.luse@gmail.com