Skip to content
Merged
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
1,858 changes: 1,469 additions & 389 deletions backend/package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"zod": "^4.3.6"
},
"scripts": {
"dev": "ts-node-dev --respawn src/index.ts",
"start": "node dist/index.js",
"build": "tsc",
"lint": "eslint 'src/**/*.ts'"
},
"devDependencies": {
Expand Down
9 changes: 1 addition & 8 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import { validateEnv } from "./validateEnv";
import { randomUUID } from "crypto";
import { z } from "zod";
import path from "path";
import { fileURLToPath } from "url";
import { config, walletIntegrationReady } from "./config";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
import {
addPledge,
calculateProgress,
Expand All @@ -26,10 +23,8 @@ import {
listCampaignPledges,
listCampaigns,
type ListCampaignsOptions,
softDeleteCampaign,
reconcileOnChainPledge,
refundContributor,
updateCampaign,
} from "./services/campaignStore";
import { checkDbHealth } from "./services/db";
import { getCampaignHistory } from "./services/eventHistory";
Expand All @@ -46,7 +41,6 @@ import {
parsePledgeListPaginationQuery,
reconcilePledgePayloadSchema,
refundPayloadSchema,
updateCampaignPayloadSchema,
zodIssuesToErrorMessage,
zodIssuesToValidationIssues,
} from "./validation/schemas";
Expand All @@ -66,7 +60,6 @@ const RATE_LIMIT_MAX_REQUESTS = 120;
const WRITE_RATE_LIMIT_MAX_REQUESTS = 40;
const CAMPAIGN_DETAIL_PLEDGE_PREVIEW_LIMIT = 5;


app.use(
cors({
origin: (origin, callback) => {
Expand Down Expand Up @@ -606,4 +599,4 @@ function startServer() {

if (require.main === module) {
startServer();
}
}
1 change: 1 addition & 0 deletions backend/src/services/campaignStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@
params.push(searchTerm);
}

if (options?.assetCode) {

Check failure on line 365 in backend/src/services/campaignStore.ts

View workflow job for this annotation

GitHub Actions / Backend Build

Unexpected any. Specify a different type
whereClauses.push(`accepted_tokens_json LIKE ?`);
params.push(`%${options.assetCode.toUpperCase()}%`);
}
Expand Down Expand Up @@ -591,6 +591,7 @@
title: input.title.trim(),
description: input.description.trim(),
acceptedTokens,
assetCode: acceptedTokens[0] || "",
targetAmount: round(input.targetAmount),
pledgedAmount: 0,
deadline: input.deadline,
Expand Down
27 changes: 12 additions & 15 deletions e2e/campaign-lifecycle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import { DashboardPage } from './dashboard';

test.describe('Campaign Lifecycle', () => {
test.beforeEach(async ({ page }) => {
// Mock Freighter API
await page.addInitScript(() => {
(window as any).freighter = {
isConnected: () => Promise.resolve(true),
requestAccess: () => Promise.resolve("GBAF7Y6PJY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY"),
requestAccess: () => Promise.resolve("GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
getNetworkDetails: () => Promise.resolve({
networkPassphrase: "Test SDF Network ; September 2015",
sorobanRpcUrl: "https://soroban-testnet.stellar.org:443"
Expand All @@ -19,47 +18,45 @@ test.describe('Campaign Lifecycle', () => {

test('should complete a full campaign lifecycle (Create -> Pledge -> Funded -> Claim)', async ({ page }) => {
const dashboard = new DashboardPage(page);
const campaignTitle = `E2E Campaign ${Date.now()}`;
const creator = "GBAF7Y6PJY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY6PY";
const campaignTitle = `E2E Test Campaign ${Date.now()}`;
const creator = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

await dashboard.goto();

// 1. Create Campaign with a very short deadline
await test.step('Create Campaign', async () => {
await dashboard.creatorInput.fill(creator);
await dashboard.titleInput.fill(campaignTitle);
await dashboard.descriptionInput.fill('This is a test campaign created by Playwright E2E test suite.');
await dashboard.descriptionInput.fill('Playwright E2E full lifecycle test campaign.');
await dashboard.targetAmountInput.fill('100');
await dashboard.deadlineHoursInput.fill('0.001'); // ~3.6 seconds
await dashboard.deadlineHoursInput.fill('0.001');

await dashboard.createButton.click();
await expect(page.locator(`text=${campaignTitle}`)).toBeVisible();
});

// 2. Select the campaign
await test.step('Select Campaign', async () => {
await dashboard.selectCampaign(campaignTitle);
await expect(page.locator('.detail-panel h2')).toHaveText(campaignTitle);
});

// 3. Connect Wallet
// Connect Wallet
await test.step('Connect Wallet', async () => {
await dashboard.connectWallet();
});

// 4. Submit Pledge (Completes Funding)
await test.step('Submit Pledge', async () => {
await dashboard.pledge('100');
await expect(page.locator('.detail-stat:has-text("Remaining") strong')).toHaveText('0');
await expect(page.locator('text=Funded')).toBeVisible();
});

// 5. Wait for deadline and Claim
await test.step('Wait for Deadline and Claim', async () => {
// Wait for the deadline to pass
await page.waitForTimeout(5000);
await page.waitForTimeout(4000);

Comment thread
GauravKarakoti marked this conversation as resolved.
// Re-select to refresh status or just try to claim
await dashboard.claim();

await expect(page.locator('text=Campaign claimed successfully')).toBeVisible();
await expect(page.locator('.detail-stat:has-text("Status")')).toContainText('Claimed');
});
});
});
});
9 changes: 4 additions & 5 deletions e2e/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ export class DashboardPage {
await this.page.goto('/');
}

async createCampaign(creator: string, title: string, description: string, target: string) {
async createCampaign(creator: string, title: string, description: string, target: string, deadlineHours: string = '24') {
await this.creatorInput.fill(creator);
await this.titleInput.fill(title);
await this.descriptionInput.fill(description);
await this.targetAmountInput.fill(target);
await this.deadlineHoursInput.fill('24');
await this.deadlineHoursInput.fill(deadlineHours);
await this.createButton.click();

// Wait for the new campaign to appear in the table
Expand All @@ -57,12 +57,11 @@ export class DashboardPage {
async pledge(amount: string) {
await this.pledgeAmountInput.fill(amount);
await this.addPledgeButton.click();
await expect(this.page.locator('text=Pledge recorded in the local goal vault')).toBeVisible();
await expect(this.page.locator('text=Pledge recorded')).toBeVisible();
}

async claim() {
await this.claimVaultButton.click();
// Wait for claimed status
await expect(this.page.locator('text=Campaign claimed successfully')).toBeVisible();
}
}
}
Loading
Loading