fix(ci): resolve E2E workflow failures and boost test coverage

E2E Workflow Fixes:

Add frontend dependency installation step (missing npm ci in frontend/)
Remove incorrect working-directory from backend build step
Update Node.js version from v18 to v20 (dependency requirements)
Backend Coverage: 84.9% → 85.0% (20+ new test functions):

Access list service validation and templates
Backup service error handling and edge cases
Security audit logs and rule sets
Auth service edge cases and token validation
Certificate service upload and sync error paths
Frontend Coverage: 85.06% → 85.66% (27 new tests):

Tabs component accessibility and keyboard navigation
Plugins page status badges and error handling
SecurityHeaders CRUD operations and presets
API wrappers for credentials and encryption endpoints
E2E Infrastructure:

Enhanced global-setup with emergency security module reset
Added retry logic and verification for settings propagation
Known Issues:

19 E2E tests still failing (ACL blocking security APIs - Issue #16)
7 Plugins modal UI tests failing (non-critical)
To be addressed in follow-up PR
Fixes #550 E2E workflow failures
Related to #16 ACL implementation
This commit is contained in:
GitHub Actions
2026-01-26 04:09:57 +00:00
parent 0b9484faf0
commit 29d2ec9cbf
20 changed files with 4280 additions and 1516 deletions

View File

@@ -0,0 +1,221 @@
import '@testing-library/jest-dom/vitest'
import { render, screen } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import userEvent from '@testing-library/user-event'
import { Tabs, TabsList, TabsTrigger, TabsContent } from './Tabs'
describe('Tabs', () => {
it('renders tabs container with proper role', () => {
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
</TabsList>
</Tabs>
)
const tablist = screen.getByRole('tablist')
expect(tablist).toBeInTheDocument()
})
it('renders all tabs with correct labels', () => {
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">First Tab</TabsTrigger>
<TabsTrigger value="tab2">Second Tab</TabsTrigger>
<TabsTrigger value="tab3">Third Tab</TabsTrigger>
</TabsList>
</Tabs>
)
expect(screen.getByRole('tab', { name: 'First Tab' })).toBeInTheDocument()
expect(screen.getByRole('tab', { name: 'Second Tab' })).toBeInTheDocument()
expect(screen.getByRole('tab', { name: 'Third Tab' })).toBeInTheDocument()
})
it('first tab is active by default', () => {
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
</Tabs>
)
const tab1 = screen.getByRole('tab', { name: 'Tab 1' })
const tab2 = screen.getByRole('tab', { name: 'Tab 2' })
expect(tab1).toHaveAttribute('data-state', 'active')
expect(tab2).toHaveAttribute('data-state', 'inactive')
})
it('clicking tab changes active state', async () => {
const user = userEvent.setup()
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
</Tabs>
)
const tab2 = screen.getByRole('tab', { name: 'Tab 2' })
await user.click(tab2)
expect(tab2).toHaveAttribute('data-state', 'active')
})
it('only one tab active at a time', async () => {
const user = userEvent.setup()
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
<TabsTrigger value="tab3">Tab 3</TabsTrigger>
</TabsList>
</Tabs>
)
const tab1 = screen.getByRole('tab', { name: 'Tab 1' })
const tab2 = screen.getByRole('tab', { name: 'Tab 2' })
const tab3 = screen.getByRole('tab', { name: 'Tab 3' })
// Initially tab1 is active
expect(tab1).toHaveAttribute('data-state', 'active')
// Click tab2
await user.click(tab2)
expect(tab2).toHaveAttribute('data-state', 'active')
expect(tab1).toHaveAttribute('data-state', 'inactive')
expect(tab3).toHaveAttribute('data-state', 'inactive')
// Click tab3
await user.click(tab3)
expect(tab3).toHaveAttribute('data-state', 'active')
expect(tab1).toHaveAttribute('data-state', 'inactive')
expect(tab2).toHaveAttribute('data-state', 'inactive')
})
it('disabled tab cannot be clicked', async () => {
const user = userEvent.setup()
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2" disabled>Tab 2</TabsTrigger>
</TabsList>
</Tabs>
)
const tab1 = screen.getByRole('tab', { name: 'Tab 1' })
const tab2 = screen.getByRole('tab', { name: 'Tab 2' })
expect(tab2).toBeDisabled()
await user.click(tab2)
// Tab 1 should still be active
expect(tab1).toHaveAttribute('data-state', 'active')
expect(tab2).toHaveAttribute('data-state', 'inactive')
})
it('keyboard navigation with arrow keys', async () => {
const user = userEvent.setup()
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
<TabsTrigger value="tab3">Tab 3</TabsTrigger>
</TabsList>
</Tabs>
)
const tab1 = screen.getByRole('tab', { name: 'Tab 1' })
const tab2 = screen.getByRole('tab', { name: 'Tab 2' })
tab1.focus()
expect(tab1).toHaveFocus()
// Arrow right should move focus and activate tab2
await user.keyboard('{ArrowRight}')
expect(tab2).toHaveFocus()
expect(tab2).toHaveAttribute('data-state', 'active')
})
it('active tab has correct aria-selected', async () => {
const user = userEvent.setup()
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
</Tabs>
)
const tab1 = screen.getByRole('tab', { name: 'Tab 1' })
const tab2 = screen.getByRole('tab', { name: 'Tab 2' })
expect(tab1).toHaveAttribute('aria-selected', 'true')
expect(tab2).toHaveAttribute('aria-selected', 'false')
await user.click(tab2)
expect(tab1).toHaveAttribute('aria-selected', 'false')
expect(tab2).toHaveAttribute('aria-selected', 'true')
})
it('tab panels show/hide based on active tab', async () => {
const user = userEvent.setup()
render(
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
<TabsContent value="tab1" data-testid="content1">Content 1</TabsContent>
<TabsContent value="tab2" data-testid="content2">Content 2</TabsContent>
</Tabs>
)
// Content 1 should be visible (active)
const content1 = screen.getByTestId('content1')
expect(content1).toBeInTheDocument()
expect(content1).toHaveAttribute('data-state', 'active')
// Content 2 should be hidden (inactive)
const content2 = screen.getByTestId('content2')
expect(content2).toBeInTheDocument()
expect(content2).toHaveAttribute('data-state', 'inactive')
const tab2 = screen.getByRole('tab', { name: 'Tab 2' })
await user.click(tab2)
// After click, content states should swap
expect(content1).toHaveAttribute('data-state', 'inactive')
expect(content2).toHaveAttribute('data-state', 'active')
})
it('custom className is applied', () => {
render(
<Tabs defaultValue="tab1">
<TabsList className="custom-list-class">
<TabsTrigger value="tab1" className="custom-trigger-class">Tab 1</TabsTrigger>
</TabsList>
<TabsContent value="tab1" className="custom-content-class">Content</TabsContent>
</Tabs>
)
const tablist = screen.getByRole('tablist')
const tab = screen.getByRole('tab', { name: 'Tab 1' })
const content = screen.getByText('Content')
expect(tablist).toHaveClass('custom-list-class')
expect(tab).toHaveClass('custom-trigger-class')
expect(content).toHaveClass('custom-content-class')
})
})