Skip to content

Commit 16a9dc0

Browse files
authored
Merge pull request #26 from hasanpeal/develop
Client and server tests added
2 parents bf39dbf + c5a0a33 commit 16a9dc0

31 files changed

+3901
-44
lines changed

.github/workflows/ci.yml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: CI Pipeline
2+
3+
on:
4+
push:
5+
branches: [main, develop, master]
6+
pull_request:
7+
branches: [main, develop, master]
8+
workflow_dispatch:
9+
10+
jobs:
11+
# Backend Tests
12+
backend-tests:
13+
name: Backend Tests
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Setup .NET
21+
uses: actions/setup-dotnet@v4
22+
with:
23+
dotnet-version: "9.0.x"
24+
25+
- name: Cache NuGet packages
26+
uses: actions/cache@v4
27+
with:
28+
path: ~/.nuget/packages
29+
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
30+
restore-keys: |
31+
${{ runner.os }}-nuget-
32+
33+
- name: Restore dependencies
34+
run: dotnet restore server/server.sln
35+
working-directory: .
36+
37+
- name: Build solution
38+
run: dotnet build server/server.sln --no-restore --configuration Release
39+
working-directory: .
40+
41+
- name: Run tests
42+
run: dotnet test server/Tests/server.Tests.csproj --no-build --configuration Release --verbosity normal --logger "trx;LogFileName=test-results.trx"
43+
working-directory: .
44+
45+
- name: Upload test results
46+
uses: actions/upload-artifact@v4
47+
if: always()
48+
with:
49+
name: test-results
50+
path: server/Tests/TestResults/**/*
51+
retention-days: 7
52+
53+
# Frontend Build and Lint
54+
frontend-build:
55+
name: Frontend Build & Lint
56+
runs-on: ubuntu-latest
57+
58+
steps:
59+
- name: Checkout code
60+
uses: actions/checkout@v4
61+
62+
- name: Setup Node.js
63+
uses: actions/setup-node@v4
64+
with:
65+
node-version: "20"
66+
cache: "npm"
67+
cache-dependency-path: client/package-lock.json
68+
69+
- name: Install dependencies
70+
run: npm ci
71+
working-directory: client
72+
73+
- name: Run linter
74+
run: npm run lint
75+
working-directory: client
76+
77+
- name: Build Next.js app
78+
run: npm run build
79+
working-directory: client
80+
env:
81+
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL || 'http://localhost:5000/api' }}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Test with Coverage
2+
3+
on:
4+
# Disabled - using ci.yml instead. Enable this if you want coverage on every push
5+
# push:
6+
# branches: [main, develop, master]
7+
# pull_request:
8+
# branches: [main, develop, master]
9+
workflow_dispatch: # Manual trigger for coverage reports
10+
11+
jobs:
12+
test-coverage:
13+
name: Test with Code Coverage
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Setup .NET
21+
uses: actions/setup-dotnet@v4
22+
with:
23+
dotnet-version: "9.0.x"
24+
25+
- name: Restore dependencies
26+
run: dotnet restore server/server.sln
27+
working-directory: .
28+
29+
- name: Build solution
30+
run: dotnet build server/server.sln --no-restore --configuration Release
31+
working-directory: .
32+
33+
- name: Install ReportGenerator
34+
run: dotnet tool install -g dotnet-reportgenerator-globaltool
35+
36+
- name: Run tests with coverage
37+
run: |
38+
dotnet test server/Tests/server.Tests.csproj \
39+
--no-build \
40+
--configuration Release \
41+
--verbosity normal \
42+
--collect:"XPlat Code Coverage" \
43+
--results-directory:./coverage
44+
working-directory: .
45+
46+
- name: Generate coverage report
47+
run: |
48+
reportgenerator \
49+
-reports:"./coverage/**/coverage.cobertura.xml" \
50+
-targetdir:"./coverage-report" \
51+
-reporttypes:"Html;Badges;JsonSummary"
52+
working-directory: .
53+
54+
- name: Upload coverage to Codecov
55+
uses: codecov/codecov-action@v4
56+
if: always()
57+
with:
58+
files: ./coverage/**/coverage.cobertura.xml
59+
flags: backend
60+
name: backend-coverage
61+
fail_ci_if_error: false
62+
63+
- name: Upload coverage report
64+
uses: actions/upload-artifact@v4
65+
if: always()
66+
with:
67+
name: coverage-report
68+
path: coverage-report/
69+
retention-days: 30
70+
71+
- name: Comment PR with coverage
72+
uses: py-cov-action/python-coverage-comment-action@v3
73+
if: github.event_name == 'pull_request'
74+
with:
75+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
76+
COVERAGE_FILES: coverage-report/Summary.json

.github/workflows/test.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Run Tests
2+
3+
on:
4+
# Disabled - using ci.yml instead
5+
# push:
6+
# branches: [main, develop, master]
7+
# pull_request:
8+
# branches: [main, develop, master]
9+
workflow_dispatch: # Allows manual triggering
10+
11+
jobs:
12+
test:
13+
name: Run Backend Tests
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Setup .NET
21+
uses: actions/setup-dotnet@v4
22+
with:
23+
dotnet-version: "9.0.x"
24+
25+
- name: Restore dependencies
26+
run: dotnet restore server/server.csproj
27+
working-directory: .
28+
29+
- name: Restore test dependencies
30+
run: dotnet restore server/Tests/server.Tests.csproj
31+
working-directory: .
32+
33+
- name: Build solution
34+
run: dotnet build server/server.sln --no-restore --configuration Release
35+
working-directory: .
36+
37+
- name: Run tests
38+
run: dotnet test server/Tests/server.Tests.csproj --no-build --configuration Release --verbosity normal --logger "trx;LogFileName=test-results.trx" --collect:"XPlat Code Coverage"
39+
working-directory: .
40+
41+
- name: Upload test results
42+
uses: actions/upload-artifact@v4
43+
if: always()
44+
with:
45+
name: test-results
46+
path: server/Tests/TestResults/**/*
47+
retention-days: 30
48+
49+
- name: Publish test results
50+
uses: EnricoMi/publish-unit-test-result-action@v2
51+
if: always()
52+
with:
53+
files: |
54+
server/Tests/TestResults/**/*.trx
55+
check_name: "Backend Test Results"

client/app/about/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export default function AboutPage() {
6565
<p className="text-slate-300 leading-relaxed text-lg">
6666
Eloomen was built to solve a critical real-world problem: how to securely
6767
share sensitive information with specific people, either immediately or at
68-
a future time. Whether you're planning your digital estate, sharing important
68+
a future time. Whether you&apos;re planning your digital estate, sharing important
6969
documents with family, or managing access to critical information, Eloomen
7070
gives you complete control over who sees what and when.
7171
</p>

client/app/account/page.tsx

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
import toast from "react-hot-toast";
1414
import {
1515
User,
16-
Mail,
1716
Smartphone,
1817
Activity,
1918
Trash2,
@@ -35,7 +34,7 @@ export default function AccountPage() {
3534
const [activeTab, setActiveTab] = useState<Tab>("profile");
3635
const [devices, setDevices] = useState<UserDevice[]>([]);
3736
const [logs, setLogs] = useState<AccountLog[]>([]);
38-
const [loading, setLoading] = useState(true);
37+
const [loading] = useState(true);
3938
const [devicesLoading, setDevicesLoading] = useState(false);
4039
const [logsLoading, setLogsLoading] = useState(false);
4140
const [editingField, setEditingField] = useState<"username" | "email" | null>(
@@ -72,7 +71,6 @@ export default function AccountPage() {
7271
username: user.username || "",
7372
email: user.email || "",
7473
});
75-
loadInitialData();
7674
}
7775
}, [isAuthenticated, user]);
7876

@@ -83,22 +81,9 @@ export default function AccountPage() {
8381
if (activeTab === "logs" && logs.length === 0) {
8482
loadLogs();
8583
}
84+
// eslint-disable-next-line react-hooks/exhaustive-deps
8685
}, [activeTab]);
8786

88-
const loadInitialData = async () => {
89-
try {
90-
setLoading(true);
91-
await Promise.all([loadDevices(), loadLogs()]);
92-
} catch (error) {
93-
if (error instanceof SessionExpiredError) {
94-
return;
95-
}
96-
toast.error("Failed to load account");
97-
} finally {
98-
setLoading(false);
99-
}
100-
};
101-
10287
const loadDevices = async () => {
10388
try {
10489
setDevicesLoading(true);
@@ -139,9 +124,7 @@ export default function AccountPage() {
139124
}
140125

141126
await apiClient.updateProfile(updateData);
142-
toast.success(
143-
`${field === "username" ? "Username" : "Email"} updated`
144-
);
127+
toast.success(`${field === "username" ? "Username" : "Email"} updated`);
145128
setEditingField(null);
146129

147130
// Refresh user data

client/app/components/CreateVaultItemModal.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
ItemPermission,
1111
ItemVisibilityRequest,
1212
SessionExpiredError,
13+
WalletType,
1314
} from "../lib/api";
1415
import toast from "react-hot-toast";
1516

@@ -162,7 +163,7 @@ export default function CreateVaultItemModal({
162163
setVisibilities(defaultVisibilities);
163164
}
164165
}
165-
}, [isOpen, editingItem, members]);
166+
}, [isOpen, editingItem, members, currentUserEmail]);
166167

167168
if (!isOpen) return null;
168169

@@ -197,7 +198,7 @@ export default function CreateVaultItemModal({
197198
noteContent: noteContent || undefined,
198199
url: url || undefined,
199200
linkNotes: linkNotes || undefined,
200-
walletType: walletType as any,
201+
walletType: walletType as WalletType,
201202
platformName: platformName || undefined,
202203
blockchain: blockchain || undefined,
203204
publicAddress: publicAddress || undefined,
@@ -238,7 +239,7 @@ export default function CreateVaultItemModal({
238239
noteContent: noteContent || undefined,
239240
url: url || undefined,
240241
linkNotes: linkNotes || undefined,
241-
walletType: walletType as any,
242+
walletType: walletType as WalletType,
242243
platformName: platformName || undefined,
243244
blockchain: blockchain || undefined,
244245
publicAddress: publicAddress || undefined,
@@ -252,12 +253,12 @@ export default function CreateVaultItemModal({
252253

253254
onSuccess();
254255
onClose();
255-
} catch (error: any) {
256+
} catch (error: unknown) {
256257
// Don't show toast for session expiration - it's already handled in API client
257258
if (error instanceof SessionExpiredError) {
258259
return;
259260
}
260-
toast.error(error.message || "Failed to save item");
261+
toast.error(error instanceof Error ? error.message : "Failed to save item");
261262
} finally {
262263
setLoading(false);
263264
}
@@ -449,7 +450,7 @@ export default function CreateVaultItemModal({
449450
</label>
450451
<select
451452
value={walletType}
452-
onChange={(e) => setWalletType(e.target.value as any)}
453+
onChange={(e) => setWalletType(e.target.value as WalletType)}
453454
className="w-full bg-slate-900/50 border border-slate-700 rounded-lg px-4 py-2 text-slate-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent cursor-pointer"
454455
>
455456
<option value="SeedPhrase">Seed Phrase</option>

client/app/components/NotificationsModal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ export default function NotificationsModal({
2727

2828
useEffect(() => {
2929
if (isOpen) {
30-
setSelectedNotifications(new Set());
30+
// Use setTimeout to avoid calling setState synchronously in effect
31+
setTimeout(() => {
32+
setSelectedNotifications(new Set());
33+
}, 0);
3134
}
3235
}, [isOpen]);
3336

client/app/components/ViewItemModal.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22

33
import { useState, useEffect } from "react";
44
import { VaultItem, apiClient } from "../lib/api";
5-
import { FileText, Key, StickyNote, Link as LinkIcon, Wallet, Package, X } from "lucide-react";
5+
import {
6+
FileText,
7+
Key,
8+
StickyNote,
9+
Link as LinkIcon,
10+
Wallet,
11+
Package,
12+
X,
13+
} from "lucide-react";
614

715
interface ViewItemModalProps {
816
isOpen: boolean;
@@ -21,7 +29,7 @@ export default function ViewItemModal({
2129
vaultId,
2230
onEdit,
2331
onDelete,
24-
canEdit = false,
32+
canEdit = false, // eslint-disable-line @typescript-eslint/no-unused-vars
2533
}: ViewItemModalProps) {
2634
const [fullItem, setFullItem] = useState<VaultItem | null>(item);
2735
const [loading, setLoading] = useState(false);
@@ -392,4 +400,3 @@ export default function ViewItemModal({
392400
</div>
393401
);
394402
}
395-

client/app/dashboard/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import ContactModal from "../components/ContactModal";
1717
import NotificationsModal from "../components/NotificationsModal";
1818

1919
export default function DashboardPage() {
20-
const { isLoading, isAuthenticated, user, logout } = useAuth();
20+
const { isLoading, isAuthenticated, logout } = useAuth();
2121
const router = useRouter();
2222
const [vaults, setVaults] = useState<Vault[]>([]);
2323
const [loading, setLoading] = useState(true);

0 commit comments

Comments
 (0)