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
846 changes: 272 additions & 574 deletions FEATURES.md

Large diffs are not rendered by default.

872 changes: 144 additions & 728 deletions README.md

Large diffs are not rendered by default.

770 changes: 213 additions & 557 deletions ROADMAP.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions site/features.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ <h3>Auto-Hibernation</h3>

<div class="feature-card">
<div class="feature-icon">🖥️</div>
<h3>Multi-Platform Support</h3>
<p>Deploy on Kubernetes, Docker, or hybrid environments. Event-driven architecture with NATS JetStream for platform coordination.</p>
<h3>Kubernetes-Native Platform</h3>
<p>Built for Kubernetes with Custom Resource Definitions (CRDs), native auto-scaling, and Helm chart deployment. Docker support is planned for future releases.</p>
<ul style="margin-top: 1rem; color: var(--text-secondary);">
<li>Kubernetes controller with CRDs</li>
<li>Docker controller for standalone hosts</li>
<li>NATS JetStream messaging</li>
<li>Helm chart for K8s deployment</li>
<li>Production-ready K8s controller</li>
<li>Session and Template CRDs</li>
<li>Helm chart for deployment</li>
<li>Docker support (planned)</li>
</ul>
</div>

Expand Down
22 changes: 12 additions & 10 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@
<section class="hero">
<div class="hero-content">
<h1>Stream Any App to Your Browser</h1>
<p>Kubernetes-native platform for delivering containerized applications with browser-based access, auto-hibernation, and a powerful plugin system.</p>
<p class="mt-2" style="font-size: 1.1rem; opacity: 0.9;">🚀 <strong>Coming Soon:</strong> Managed SaaS offering - Skip the setup and start streaming apps in minutes!</p>
<p>Kubernetes-native platform for delivering containerized applications with browser-based access, auto-hibernation, and enterprise security features.</p>
<p class="mt-2" style="font-size: 1.1rem; opacity: 0.9; background: rgba(255,255,255,0.1); padding: 0.75rem 1rem; border-radius: 8px; display: inline-block;">
<strong>v1.0.0-beta</strong> - Core Kubernetes platform functional. <a href="https://github.com/JoshuaAFerguson/streamspace/blob/main/ROADMAP.md" style="color: #a5b4fc;">See roadmap</a>
</p>
<div class="hero-buttons">
<a href="getting-started.html" class="button button-primary">Get Started</a>
<a href="https://github.com/JoshuaAFerguson/streamspace" class="button button-secondary">View on GitHub</a>
Expand All @@ -63,8 +65,8 @@ <h3>Browser-Based Access</h3>
</div>
<div class="feature-card">
<div class="feature-icon">🖥️</div>
<h3>Multi-Platform</h3>
<p>Deploy on Kubernetes, Docker, or hybrid environments. Event-driven architecture with NATS messaging.</p>
<h3>Kubernetes-Native</h3>
<p>Built for Kubernetes with CRDs, auto-scaling, and native resource management. Docker support planned.</p>
</div>
<div class="feature-card">
<div class="feature-icon">⚡</div>
Expand All @@ -73,8 +75,8 @@ <h3>Auto-Hibernation</h3>
</div>
<div class="feature-card">
<div class="feature-icon">🔌</div>
<h3>Plugin System</h3>
<p>Extend functionality with plugins for webhooks, external integrations, custom UI themes, and more.</p>
<h3>Plugin Framework</h3>
<p>Extensible plugin architecture for webhooks, integrations, and UI themes. Plugin implementations in progress.</p>
</div>
<div class="feature-card">
<div class="feature-icon">👥</div>
Expand Down Expand Up @@ -159,16 +161,16 @@ <h2>Architecture</h2>
</div>
<div class="features-grid mt-4">
<div class="feature-card">
<h3>Platform Controllers</h3>
<p>Kubernetes and Docker controllers manage sessions on their respective platforms via NATS events.</p>
<h3>Kubernetes Controller</h3>
<p>Production-ready controller for session lifecycle, auto-hibernation, and resource management.</p>
</div>
<div class="feature-card">
<h3>Go API Backend</h3>
<p>REST + WebSocket API with PostgreSQL for caching, repository sync, and plugin management.</p>
<p>70+ handlers, 87 database tables, WebSocket support, and comprehensive authentication.</p>
</div>
<div class="feature-card">
<h3>React UI</h3>
<p>TypeScript-based web interface with Material-UI, real-time updates, and admin dashboard.</p>
<p>50+ components with Material-UI, real-time updates, and full admin dashboard.</p>
</div>
</div>
</div>
Expand Down
File renamed without changes.
10 changes: 7 additions & 3 deletions ui/src/components/SessionCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
user: 'testuser',
template: 'firefox-browser',
state: 'running',
phase: 'Running',
status: {
phase: 'Running',
},
url: 'https://test-session.streamspace.local',
createdAt: '2025-01-15T10:00:00Z',
resources: {
memory: '2Gi',
cpu: '1000m',
},
isActive: true,
isIdle: false,
};

describe('SessionCard Component', () => {
Expand All @@ -29,7 +33,7 @@
expect(screen.getByText(/firefox-browser/i)).toBeInTheDocument();

// Check if state is displayed
expect(screen.getByText(/running/i)).toBeInTheDocument();

Check failure on line 36 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > renders session information correctly

TestingLibraryElementError: Found multiple elements with the text: /running/i Here are the matching elements: Ignored nodes: comments, script, style <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > running </span> Ignored nodes: comments, script, style <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > Running </span> (If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)). Ignored nodes: comments, script, style <body> <div> <div class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-bhp9pd-MuiPaper-root-MuiCard-root" > <div class="MuiCardContent-root css-46bh2p-MuiCardContent-root" > <div class="MuiBox-root css-u0v7vs" > <div class="MuiBox-root css-0" > <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" > firefox-browser </h6> <span class="MuiTypography-root MuiTypography-caption css-ol7wxs-MuiTypography-root" > test-session </span> </div> <div class="MuiBox-root css-11ei6nx" > <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > running </span> </div> <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > Running </span> </div> <div aria-label="Session is active" class="MuiChip-root MuiChip-outlined MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-outlinedSuccess css-jdl1l1-MuiChip-root" data-mui-internal-clone-element="true" > <svg aria-hidden="true" class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiChip-icon MuiChip-iconSmall MuiChip-iconColorSuccess css-i4bv87-MuiSvgIcon-root" data-testid="CircleIcon" focusable="false" style="font-size: 12px;" viewBox="0 0 24 24" > <path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2" /> </svg> <span class="MuiChip-label MuiChip-labelSmall css-rrn746-MuiChip-label" > Active </span> </div> </div> </div> <div class="MuiBox-root css-1821gv5" > <div class="MuiBox-root css-gg4vpm" > <p class="MuiTypography-root MuiTypography-body2 css-r40f8v-MuiTypography-root" > Resources </p> <p class="MuiTypography-root MuiTypography-body2 css-e784if-MuiTypography-root" > 2Gi / 1000m </p> </div> </div> </div> <div class="MuiCardActions-root MuiCardActions-spacing css-1j9t6id-MuiCardActions-root" > <div class="MuiBox-root css-0" > <button class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary css-1knaqv7-MuiButtonBase-root-MuiButton
});

it('displays resource usage', () => {
Expand All @@ -56,7 +60,7 @@
const onHibernate = vi.fn();
render(<SessionCard session={mockSession} onHibernate={onHibernate} />);

const hibernateButton = screen.getByRole('button', { name: /hibernate/i });

Check failure on line 63 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > calls onHibernate when hibernate button is clicked

TestingLibraryElementError: Unable to find an accessible element with the role "button" and name `/hibernate/i` Here are the accessible roles: heading: Name "firefox-browser": <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" /> -------------------------------------------------- button: Name "Connect": <button class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary css-1knaqv7-MuiButtonBase-root-MuiButton-root" tabindex="0" type="button" /> Name "": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorWarning MuiIconButton-sizeSmall css-1782d2u-MuiButtonBase-root-MuiIconButton-root" tabindex="0" type="button" /> Name "Share with User": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Share with User" type="button" /> Name "Create Invitation Link": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Create Invitation Link" type="button" /> Name "Manage Tags": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Manage Tags" type="button" /> Name "Delete Session": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorError MuiIconButton-sizeSmall css-1mk6yu1-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Delete Session" type="button" /> -------------------------------------------------- Ignored nodes: comments, script, style <body> <div> <div class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-bhp9pd-MuiPaper-root-MuiCard-root" > <div class="MuiCardContent-root css-46bh2p-MuiCardContent-root" > <div class="MuiBox-root css-u0v7vs" > <div class="MuiBox-root css-0" > <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" > firefox-browser </h6> <span class="MuiTypography-root MuiTypography-caption css-ol7wxs-MuiTypography-root" > test-session </span> </div> <div class="MuiBox-root css-11ei6nx" > <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > running </span> </div> <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > Running </span> </div> <div aria-label="Session is active" class="MuiChip-root MuiChip-outlined MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-outlinedSuccess css-jdl1l1-MuiChip-root" data-mui-internal-clone-element="true" > <svg aria-hidden="true" class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiChip-icon MuiChip-iconSmall MuiChip-iconColorSuccess css-i4bv87-MuiSvgIcon-root" data-testid="CircleIcon" focusable="f
fireEvent.click(hibernateButton);

expect(onHibernate).toHaveBeenCalledWith(mockSession.id);
Expand All @@ -66,7 +70,7 @@
const onTerminate = vi.fn();
render(<SessionCard session={mockSession} onTerminate={onTerminate} />);

const terminateButton = screen.getByRole('button', { name: /terminate/i });

Check failure on line 73 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > calls onTerminate when terminate button is clicked

TestingLibraryElementError: Unable to find an accessible element with the role "button" and name `/terminate/i` Here are the accessible roles: heading: Name "firefox-browser": <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" /> -------------------------------------------------- button: Name "Connect": <button class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary css-1knaqv7-MuiButtonBase-root-MuiButton-root" tabindex="0" type="button" /> Name "": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorWarning MuiIconButton-sizeSmall css-1782d2u-MuiButtonBase-root-MuiIconButton-root" tabindex="0" type="button" /> Name "Share with User": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Share with User" type="button" /> Name "Create Invitation Link": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Create Invitation Link" type="button" /> Name "Manage Tags": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Manage Tags" type="button" /> Name "Delete Session": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorError MuiIconButton-sizeSmall css-1mk6yu1-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Delete Session" type="button" /> -------------------------------------------------- Ignored nodes: comments, script, style <body> <div> <div class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-bhp9pd-MuiPaper-root-MuiCard-root" > <div class="MuiCardContent-root css-46bh2p-MuiCardContent-root" > <div class="MuiBox-root css-u0v7vs" > <div class="MuiBox-root css-0" > <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" > firefox-browser </h6> <span class="MuiTypography-root MuiTypography-caption css-ol7wxs-MuiTypography-root" > test-session </span> </div> <div class="MuiBox-root css-11ei6nx" > <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > running </span> </div> <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > Running </span> </div> <div aria-label="Session is active" class="MuiChip-root MuiChip-outlined MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-outlinedSuccess css-jdl1l1-MuiChip-root" data-mui-internal-clone-element="true" > <svg aria-hidden="true" class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiChip-icon MuiChip-iconSmall MuiChip-iconColorSuccess css-i4bv87-MuiSvgIcon-root" data-testid="CircleIcon" focusable="f
fireEvent.click(terminateButton);

expect(onTerminate).toHaveBeenCalledWith(mockSession.id);
Expand All @@ -79,11 +83,11 @@
const connectButton = screen.getByRole('button', { name: /connect/i });
fireEvent.click(connectButton);

expect(onConnect).toHaveBeenCalledWith(mockSession.url);

Check failure on line 86 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > calls onConnect when connect button is clicked

AssertionError: expected "spy" to be called with arguments: [ Array(1) ] Received: 1st spy call: Array [ - "https://test-session.streamspace.local", + Object { + "createdAt": "2025-01-15T10:00:00Z", + "id": "session-1", + "isActive": true, + "isIdle": false, + "name": "test-session", + "resources": Object { + "cpu": "1000m", + "memory": "2Gi", + }, + "state": "running", + "status": Object { + "phase": "Running", + }, + "template": "firefox-browser", + "url": "https://test-session.streamspace.local", + "user": "testuser", + }, ] Number of calls: 1 ❯ src/components/SessionCard.test.tsx:86:23
});

it('disables actions for hibernated session', () => {
const hibernatedSession = { ...mockSession, state: 'hibernated', phase: 'Hibernated' };
const hibernatedSession = { ...mockSession, state: 'hibernated', status: { phase: 'Hibernated' } };
render(<SessionCard session={hibernatedSession} />);

// Connect button should be disabled or not present
Expand All @@ -94,11 +98,11 @@
});

it('shows wake button for hibernated session', () => {
const hibernatedSession = { ...mockSession, state: 'hibernated', phase: 'Hibernated' };
const hibernatedSession = { ...mockSession, state: 'hibernated', status: { phase: 'Hibernated' } };
const onWake = vi.fn();
render(<SessionCard session={hibernatedSession} onWake={onWake} />);

const wakeButton = screen.getByRole('button', { name: /wake/i });

Check failure on line 105 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > shows wake button for hibernated session

TestingLibraryElementError: Unable to find an accessible element with the role "button" and name `/wake/i` Here are the accessible roles: heading: Name "firefox-browser": <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" /> -------------------------------------------------- button: Name "": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorSuccess MuiIconButton-sizeSmall css-15ghhi9-MuiButtonBase-root-MuiIconButton-root" tabindex="0" type="button" /> Name "Share with User": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Share with User" type="button" /> Name "Create Invitation Link": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Create Invitation Link" type="button" /> Name "Manage Tags": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeSmall css-2rshpx-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Manage Tags" type="button" /> Name "Delete Session": <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorError MuiIconButton-sizeSmall css-1mk6yu1-MuiButtonBase-root-MuiIconButton-root" tabindex="0" title="Delete Session" type="button" /> -------------------------------------------------- Ignored nodes: comments, script, style <body> <div> <div class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-bhp9pd-MuiPaper-root-MuiCard-root" > <div class="MuiCardContent-root css-46bh2p-MuiCardContent-root" > <div class="MuiBox-root css-u0v7vs" > <div class="MuiBox-root css-0" > <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" > firefox-browser </h6> <span class="MuiTypography-root MuiTypography-caption css-ol7wxs-MuiTypography-root" > test-session </span> </div> <div class="MuiBox-root css-11ei6nx" > <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorWarning MuiChip-filledWarning css-1d8sx49-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > hibernated </span> </div> <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorWarning MuiChip-filledWarning css-1d8sx49-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > Hibernated </span> </div> <div aria-label="Session is hibernated to save resources" class="MuiChip-root MuiChip-outlined MuiChip-sizeSmall MuiChip-colorDefault MuiChip-outlinedDefault css-1gyxgoa-MuiChip-root" data-mui-internal-clone-element="true" > <svg aria-hidden="true" class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiChip-icon MuiChip-iconSmall MuiChip-iconColorDefault css-i4bv87-MuiSvgIcon-root" data-testid="BedtimeIcon" focusable="false" style="font-size: 12px;" viewBox="0 0 24 24" > <path d="M12.34 2.02C6.59 1.82 2 6.42 2 12c0 5.52 4.48 10 10 10 3.71 0 6.93-2.02 8.66-5.02-7.51-.25-12.09-8.43-8.32-14.96" /> </svg> <span class="MuiChip-label MuiC
expect(wakeButton).toBeInTheDocument();

fireEvent.click(wakeButton);
Expand All @@ -110,7 +114,7 @@

// Check if created date is formatted (implementation-specific)
// This would depend on how dates are displayed in the component
const dateElement = screen.getByText(/Jan 15, 2025/i);

Check failure on line 117 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > formats timestamps correctly

TestingLibraryElementError: Unable to find an element with the text: /Jan 15, 2025/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. Ignored nodes: comments, script, style <body> <div> <div class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-bhp9pd-MuiPaper-root-MuiCard-root" > <div class="MuiCardContent-root css-46bh2p-MuiCardContent-root" > <div class="MuiBox-root css-u0v7vs" > <div class="MuiBox-root css-0" > <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" > firefox-browser </h6> <span class="MuiTypography-root MuiTypography-caption css-ol7wxs-MuiTypography-root" > test-session </span> </div> <div class="MuiBox-root css-11ei6nx" > <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > running </span> </div> <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > Running </span> </div> <div aria-label="Session is active" class="MuiChip-root MuiChip-outlined MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-outlinedSuccess css-jdl1l1-MuiChip-root" data-mui-internal-clone-element="true" > <svg aria-hidden="true" class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiChip-icon MuiChip-iconSmall MuiChip-iconColorSuccess css-i4bv87-MuiSvgIcon-root" data-testid="CircleIcon" focusable="false" style="font-size: 12px;" viewBox="0 0 24 24" > <path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2" /> </svg> <span class="MuiChip-label MuiChip-labelSmall css-rrn746-MuiChip-label" > Active </span> </div> </div> </div> <div class="MuiBox-root css-1821gv5" > <div class="MuiBox-root css-gg4vpm" > <p class="MuiTypography-root MuiTypography-body2 css-r40f8v-MuiTypography-root" > Resources </p> <p class="MuiTypography-root MuiTypography-body2 css-e784if-MuiTypography-root" > 2Gi / 1000m </p> </div> </div> </div> <div class="MuiCardActions-root MuiCardActions-spacing css-1j9t6id-MuiCardActions-root" > <div class="MuiBox-root css-0" > <button class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary css-1knaqv7-MuiButtonBase-root-MuiButton-root" tabindex="0" type="button" > <span class="MuiButton-icon MuiButton-startIcon MuiButton-iconSizeSmall css-y6rp3m-MuiButton-startIcon" > <svg aria-hidden
expect(dateElement).toBeInTheDocument();
});

Expand All @@ -121,7 +125,7 @@
// Connect button should be disabled if no URL
const connectButton = screen.queryByRole('button', { name: /connect/i });
if (connectButton) {
expect(connectButton).toBeDisabled();

Check failure on line 128 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > handles missing URL gracefully

Error: expect(element).toBeDisabled() Received element is not disabled: <button class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary css-1knaqv7-MuiButtonBase-root-MuiButton-root" tabindex="0" type="button" /> ❯ src/components/SessionCard.test.tsx:128:29
}
});

Expand All @@ -129,14 +133,14 @@
const loadingSession = { ...mockSession, phase: 'Pending' };
render(<SessionCard session={loadingSession} />);

expect(screen.getByText(/pending/i)).toBeInTheDocument();

Check failure on line 136 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > displays loading state

TestingLibraryElementError: Unable to find an element with the text: /pending/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. Ignored nodes: comments, script, style <body> <div> <div class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-bhp9pd-MuiPaper-root-MuiCard-root" > <div class="MuiCardContent-root css-46bh2p-MuiCardContent-root" > <div class="MuiBox-root css-u0v7vs" > <div class="MuiBox-root css-0" > <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" > firefox-browser </h6> <span class="MuiTypography-root MuiTypography-caption css-ol7wxs-MuiTypography-root" > test-session </span> </div> <div class="MuiBox-root css-11ei6nx" > <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > running </span> </div> <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > Running </span> </div> <div aria-label="Session is active" class="MuiChip-root MuiChip-outlined MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-outlinedSuccess css-jdl1l1-MuiChip-root" data-mui-internal-clone-element="true" > <svg aria-hidden="true" class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiChip-icon MuiChip-iconSmall MuiChip-iconColorSuccess css-i4bv87-MuiSvgIcon-root" data-testid="CircleIcon" focusable="false" style="font-size: 12px;" viewBox="0 0 24 24" > <path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2" /> </svg> <span class="MuiChip-label MuiChip-labelSmall css-rrn746-MuiChip-label" > Active </span> </div> </div> </div> <div class="MuiBox-root css-1821gv5" > <div class="MuiBox-root css-gg4vpm" > <p class="MuiTypography-root MuiTypography-body2 css-r40f8v-MuiTypography-root" > Resources </p> <p class="MuiTypography-root MuiTypography-body2 css-e784if-MuiTypography-root" > 2Gi / 1000m </p> </div> </div> </div> <div class="MuiCardActions-root MuiCardActions-spacing css-1j9t6id-MuiCardActions-root" > <div class="MuiBox-root css-0" > <button class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary css-1knaqv7-MuiButtonBase-root-MuiButton-root" tabindex="0" type="button" > <span class="MuiButton-icon MuiButton-startIcon MuiButton-iconSizeSmall css-y6rp3m-MuiButton-startIcon" > <svg aria-hidden="tru
});

it('displays error state', () => {
const failedSession = { ...mockSession, phase: 'Failed', error: 'Pod failed to start' };
render(<SessionCard session={failedSession} />);

expect(screen.getByText(/failed/i)).toBeInTheDocument();

Check failure on line 143 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Component > displays error state

TestingLibraryElementError: Unable to find an element with the text: /failed/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. Ignored nodes: comments, script, style <body> <div> <div class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-bhp9pd-MuiPaper-root-MuiCard-root" > <div class="MuiCardContent-root css-46bh2p-MuiCardContent-root" > <div class="MuiBox-root css-u0v7vs" > <div class="MuiBox-root css-0" > <h6 class="MuiTypography-root MuiTypography-h6 css-1y8v3j7-MuiTypography-root" > firefox-browser </h6> <span class="MuiTypography-root MuiTypography-caption css-ol7wxs-MuiTypography-root" > test-session </span> </div> <div class="MuiBox-root css-11ei6nx" > <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > running </span> </div> <div class="MuiChip-root MuiChip-filled MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-filledSuccess css-txuvm7-MuiChip-root" > <span class="MuiChip-label MuiChip-labelSmall css-wjsjww-MuiChip-label" > Running </span> </div> <div aria-label="Session is active" class="MuiChip-root MuiChip-outlined MuiChip-sizeSmall MuiChip-colorSuccess MuiChip-outlinedSuccess css-jdl1l1-MuiChip-root" data-mui-internal-clone-element="true" > <svg aria-hidden="true" class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiChip-icon MuiChip-iconSmall MuiChip-iconColorSuccess css-i4bv87-MuiSvgIcon-root" data-testid="CircleIcon" focusable="false" style="font-size: 12px;" viewBox="0 0 24 24" > <path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2" /> </svg> <span class="MuiChip-label MuiChip-labelSmall css-rrn746-MuiChip-label" > Active </span> </div> </div> </div> <div class="MuiBox-root css-1821gv5" > <div class="MuiBox-root css-gg4vpm" > <p class="MuiTypography-root MuiTypography-body2 css-r40f8v-MuiTypography-root" > Resources </p> <p class="MuiTypography-root MuiTypography-body2 css-e784if-MuiTypography-root" > 2Gi / 1000m </p> </div> </div> </div> <div class="MuiCardActions-root MuiCardActions-spacing css-1j9t6id-MuiCardActions-root" > <div class="MuiBox-root css-0" > <button class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButton-colorPrimary css-1knaqv7-MuiButtonBase-root-MuiButton-root" tabindex="0" type="button" > <span class="MuiButton-icon MuiButton-startIcon MuiButton-iconSizeSmall css-y6rp3m-MuiButton-startIcon" > <svg aria-hidden="true
if (failedSession.error) {
expect(screen.getByText(/Pod failed to start/i)).toBeInTheDocument();
}
Expand All @@ -149,7 +153,7 @@

const buttons = screen.getAllByRole('button');
buttons.forEach((button) => {
expect(button).toHaveAccessibleName();

Check failure on line 156 in ui/src/components/SessionCard.test.tsx

View workflow job for this annotation

GitHub Actions / Test UI

src/components/SessionCard.test.tsx > SessionCard Accessibility > has accessible name for buttons

Error: expect(element).toHaveAccessibleName() Expected element to have accessible name: undefined Received: ❯ src/components/SessionCard.test.tsx:156:22 ❯ src/components/SessionCard.test.tsx:155:13
});
});

Expand Down
15 changes: 13 additions & 2 deletions ui/src/pages/SecuritySettings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import SecuritySettings from './SecuritySettings';
import * as api from '../lib/api';
import { api } from '../lib/api';

// Mock the API module
vi.mock('../lib/api');
vi.mock('../lib/api', () => ({
api: {
setupMFA: vi.fn(),
verifyMFA: vi.fn(),
getSecurityAlerts: vi.fn(),
listMFAMethods: vi.fn(),
deleteMFAMethod: vi.fn(),
getIPWhitelist: vi.fn(),
addIPToWhitelist: vi.fn(),
removeIPFromWhitelist: vi.fn(),
},
}));

// Mock Layout component
vi.mock('../components/Layout', () => ({
Expand Down
Loading