-
-
Notifications
You must be signed in to change notification settings - Fork 84
fix: restore active organization state on page refresh and add empty … #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
16e41db
a143a77
a03f5b4
e80033e
e496a0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import { describe, it, expect, beforeEach, vi } from 'vitest' | ||
| import { renderHook, act } from '@testing-library/react' | ||
| import { AppProvider, useApp } from './AppContext' | ||
|
|
||
| // Mock services to avoid network requests | ||
| vi.mock('../services/github', () => ({ | ||
| fetchOrg: vi.fn().mockResolvedValue({ login: 'test-org', public_repos: 5 }), | ||
| fetchRepos: vi.fn().mockResolvedValue([{ name: 'test-repo', orgLogin: 'test-org' }]), | ||
| fetchContributors: vi.fn().mockResolvedValue([]), | ||
| fetchIssues: vi.fn().mockResolvedValue([]), | ||
| fetchRateLimit: vi.fn().mockResolvedValue(null), | ||
| fetchPulls: vi.fn().mockResolvedValue([]) | ||
| })) | ||
|
|
||
| vi.mock('../services/analytics', () => ({ | ||
| buildAnalyticalModel: vi.fn().mockReturnValue({ allRepos: [], totalRepos: [] }), | ||
| getTopRepositories: vi.fn().mockImplementation(repos => repos) | ||
| })) | ||
|
|
||
| describe('AppContext - localStorage safety and validation', () => { | ||
| beforeEach(() => { | ||
| localStorage.clear() | ||
| vi.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('handles invalid or non-array oe_active_orgs gracefully', async () => { | ||
| // Test with string value | ||
| localStorage.setItem('oe_active_orgs', JSON.stringify('invalid_string')) | ||
| const { result: res1 } = renderHook(() => useApp(), { wrapper: AppProvider }) | ||
| expect(res1.current.lastOrgNames).toEqual([]) | ||
|
|
||
| // Test with null | ||
| localStorage.setItem('oe_active_orgs', JSON.stringify(null)) | ||
| const { result: res2 } = renderHook(() => useApp(), { wrapper: AppProvider }) | ||
| expect(res2.current.lastOrgNames).toEqual([]) | ||
|
|
||
| // Test with mixed array including invalid items | ||
| localStorage.setItem('oe_active_orgs', JSON.stringify(['valid-org', null, 123, ' ', 'another-org'])) | ||
| let res3 | ||
| await act(async () => { | ||
| res3 = renderHook(() => useApp(), { wrapper: AppProvider }) | ||
| }) | ||
| expect(res3.result.current.lastOrgNames).toEqual(['valid-org', 'another-org']) | ||
| }) | ||
|
Comment on lines
+26
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win Test successful startup restoration. The test verifies only parsed Add a test with a valid persisted organization list. Wait for 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| it('isolates localStorage setItem failure in explore when saving oe_active_orgs', async () => { | ||
| const originalSetItem = localStorage.setItem | ||
| vi.spyOn(Storage.prototype, 'setItem').mockImplementation((key, val) => { | ||
| if (key === 'oe_active_orgs') { | ||
| throw new Error('QuotaExceededError') | ||
| } | ||
| return originalSetItem.call(localStorage, key, val) | ||
| }) | ||
|
|
||
| const { result } = renderHook(() => useApp(), { wrapper: AppProvider }) | ||
|
|
||
| let modelResult | ||
| await act(async () => { | ||
| modelResult = await result.current.explore(['test-org']) | ||
| }) | ||
|
|
||
| expect(modelResult).toBeTruthy() | ||
| expect(result.current.orgs).toHaveLength(1) | ||
| }) | ||
|
|
||
| it('isolates localStorage setItem failure in explore when saving oe_recent', async () => { | ||
| const originalSetItem = localStorage.setItem | ||
| vi.spyOn(Storage.prototype, 'setItem').mockImplementation((key, val) => { | ||
| if (key === 'oe_recent') { | ||
| throw new Error('QuotaExceededError') | ||
| } | ||
| return originalSetItem.call(localStorage, key, val) | ||
| }) | ||
|
|
||
| const { result } = renderHook(() => useApp(), { wrapper: AppProvider }) | ||
|
|
||
| let modelResult | ||
| await act(async () => { | ||
| modelResult = await result.current.explore(['test-org']) | ||
| }) | ||
|
|
||
| expect(modelResult).toBeTruthy() | ||
| expect(result.current.error).toBe('') | ||
| }) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
Repository: AOSSIE-Org/OrgExplorer
Length of output: 6569
Sensitive Data Exposure (CWE-922)
Exploitability: Difficult
Do not persist the raw GitHub PAT in
localStorage.savePat(token)stores the credential underoe_pat, where same-origin scripts can read and exfiltrate it. Use a server-side session withHttpOnlyandSecurecookies, or an equivalent token broker. Remove existingoe_patvalues during migration.🤖 Prompt for AI Agents
Sources: Path instructions, Linters/SAST tools