Skip to main content

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; use unknown if 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-testid attributes for selectors
  • Test behavior, not implementation
  • Keep tests independent

Don't:

  • Use cy.wait(3000) or page.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

  1. 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
}
}
}
`;
  1. Run codegen to generate types
  2. 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