From 9892838ca70433da17240c63dd3211a9dcd37632 Mon Sep 17 00:00:00 2001 From: Dimitris Marlagkoutsos Date: Fri, 31 Jan 2025 16:03:23 +0100 Subject: [PATCH 1/4] Add cluster config selector --- packages/extension/src/ui/App.module.css | 6 ++ .../src/ui/components/ConfigEditor.test.tsx | 23 ++++++- .../src/ui/components/ConfigEditor.tsx | 68 ++++++++++++++----- .../extension/src/vats/minimal-cluster.json | 12 ++++ .../extension/test/e2e/vat-manager.test.ts | 39 ++++++++--- vitest.config.ts | 8 +-- 6 files changed, 124 insertions(+), 32 deletions(-) create mode 100644 packages/extension/src/vats/minimal-cluster.json diff --git a/packages/extension/src/ui/App.module.css b/packages/extension/src/ui/App.module.css index 3425ab4ad..82e7123d4 100644 --- a/packages/extension/src/ui/App.module.css +++ b/packages/extension/src/ui/App.module.css @@ -494,3 +494,9 @@ div + .sent { gap: var(--spacing-sm); justify-content: flex-end; } + +.configControls { + display: flex; + gap: var(--spacing-xs); + justify-content: space-between; +} diff --git a/packages/extension/src/ui/components/ConfigEditor.test.tsx b/packages/extension/src/ui/components/ConfigEditor.test.tsx index a36e8df1d..b85934e3e 100644 --- a/packages/extension/src/ui/components/ConfigEditor.test.tsx +++ b/packages/extension/src/ui/components/ConfigEditor.test.tsx @@ -10,12 +10,13 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { ConfigEditor } from './ConfigEditor.js'; import type { KernelStatus } from '../../kernel-integration/messages.js'; -import clusterConfig from '../../vats/default-cluster.json'; +import defaultClusterConfig from '../../vats/default-cluster.json'; +import minimalClusterConfig from '../../vats/minimal-cluster.json'; import { usePanelContext } from '../context/PanelContext.js'; import { useKernelActions } from '../hooks/useKernelActions.js'; const mockStatus = { - clusterConfig, + clusterConfig: defaultClusterConfig, vats: [], }; @@ -157,7 +158,7 @@ describe('ConfigEditor Component', () => { const newStatus: KernelStatus = { clusterConfig: { - ...clusterConfig, + ...defaultClusterConfig, bootstrap: 'updated-config', }, vats: [], @@ -176,4 +177,20 @@ describe('ConfigEditor Component', () => { ); }); }); + + it('renders the config template selector with default option selected', () => { + render(); + const selector = screen.getByTestId('config-select'); + expect(selector).toBeInTheDocument(); + expect(selector).toHaveValue('Default'); + }); + + it('updates textarea when selecting a different template', async () => { + render(); + const selector = screen.getByTestId('config-select'); + const textarea = screen.getByTestId('config-textarea'); + expect(textarea).toHaveValue(JSON.stringify(defaultClusterConfig, null, 2)); + await userEvent.selectOptions(selector, 'Minimal'); + expect(textarea).toHaveValue(JSON.stringify(minimalClusterConfig, null, 2)); + }); }); diff --git a/packages/extension/src/ui/components/ConfigEditor.tsx b/packages/extension/src/ui/components/ConfigEditor.tsx index 9103067c6..2bf2a6fda 100644 --- a/packages/extension/src/ui/components/ConfigEditor.tsx +++ b/packages/extension/src/ui/components/ConfigEditor.tsx @@ -2,10 +2,22 @@ import type { ClusterConfig } from '@ocap/kernel'; import { useCallback, useEffect, useMemo, useState } from 'react'; import type { KernelStatus } from '../../kernel-integration/messages.js'; +import defaultConfig from '../../vats/default-cluster.json'; +import minimalConfig from '../../vats/minimal-cluster.json'; import styles from '../App.module.css'; import { usePanelContext } from '../context/PanelContext.js'; import { useKernelActions } from '../hooks/useKernelActions.js'; +type ConfigEntry = { + name: string; + config: ClusterConfig; +}; + +const availableConfigs: ConfigEntry[] = [ + { name: 'Default', config: defaultConfig }, + { name: 'Minimal', config: minimalConfig }, +]; + /** * Component for editing the kernel cluster configuration. * @@ -45,9 +57,14 @@ export const ConfigEditorInner: React.FC<{ status: KernelStatus }> = ({ [config, updateClusterConfig], ); - if (!config) { - return null; - } + const handleSelectConfig = useCallback((configName: string) => { + const selectedConfig = availableConfigs.find( + (item) => item.name === configName, + )?.config; + if (selectedConfig) { + setConfig(JSON.stringify(selectedConfig, null, 2)); + } + }, []); return (
@@ -59,21 +76,38 @@ export const ConfigEditorInner: React.FC<{ status: KernelStatus }> = ({ className={styles.configTextarea} data-testid="config-textarea" /> -
- - + +
); diff --git a/packages/extension/src/vats/minimal-cluster.json b/packages/extension/src/vats/minimal-cluster.json new file mode 100644 index 000000000..f072ca387 --- /dev/null +++ b/packages/extension/src/vats/minimal-cluster.json @@ -0,0 +1,12 @@ +{ + "bootstrap": "main", + "forceReset": true, + "vats": { + "main": { + "bundleSpec": "http://localhost:3000/sample-vat.bundle", + "parameters": { + "name": "MainVat" + } + } + } +} diff --git a/packages/extension/test/e2e/vat-manager.test.ts b/packages/extension/test/e2e/vat-manager.test.ts index 4e33f2fec..204a51b2a 100644 --- a/packages/extension/test/e2e/vat-manager.test.ts +++ b/packages/extension/test/e2e/vat-manager.test.ts @@ -1,7 +1,8 @@ import { test, expect } from '@playwright/test'; import type { Page, BrowserContext } from '@playwright/test'; -import clusterConfig from '../../src/vats/default-cluster.json' assert { type: 'json' }; +import defaultClusterConfig from '../../src/vats/default-cluster.json' assert { type: 'json' }; +import minimalClusterConfig from '../../src/vats/minimal-cluster.json' assert { type: 'json' }; import { makeLoadExtension } from '../helpers/extension'; test.describe('Vat Manager', () => { @@ -147,10 +148,10 @@ test.describe('Vat Manager', () => { const vatTable = popupPage.locator('[data-testid="vat-table"]'); await expect(vatTable).toBeVisible(); await expect(vatTable.locator('tr')).toHaveCount( - Object.keys(clusterConfig.vats).length + 1, // +1 for header row + Object.keys(defaultClusterConfig.vats).length + 1, // +1 for header row ); // Verify each default vat is present in the table - for (const [, vatConfig] of Object.entries(clusterConfig.vats)) { + for (const [, vatConfig] of Object.entries(defaultClusterConfig.vats)) { await expect(vatTable).toContainText(vatConfig.parameters.name); await expect(vatTable).toContainText(vatConfig.bundleSpec); } @@ -161,7 +162,7 @@ test.describe('Vat Manager', () => { const configTextarea = popupPage.locator('[data-testid="config-textarea"]'); await expect(configTextarea).toBeVisible(); await expect(configTextarea).toHaveValue( - JSON.stringify(clusterConfig, null, 2), + JSON.stringify(defaultClusterConfig, null, 2), ); // Test invalid JSON handling await configTextarea.fill('{ invalid json }'); @@ -170,12 +171,13 @@ test.describe('Vat Manager', () => { // Verify original vats still exist const vatTable = popupPage.locator('[data-testid="vat-table"]'); const firstVatKey = Object.keys( - clusterConfig.vats, - )[0] as keyof typeof clusterConfig.vats; - const originalVatName = clusterConfig.vats[firstVatKey].parameters.name; + defaultClusterConfig.vats, + )[0] as keyof typeof defaultClusterConfig.vats; + const originalVatName = + defaultClusterConfig.vats[firstVatKey].parameters.name; await expect(vatTable).toContainText(originalVatName); // Modify config with new vat name - const modifiedConfig = structuredClone(clusterConfig); + const modifiedConfig = structuredClone(defaultClusterConfig); modifiedConfig.vats[firstVatKey].parameters.name = 'SuperAlice'; // Update config and reload await configTextarea.fill(JSON.stringify(modifiedConfig, null, 2)); @@ -208,4 +210,25 @@ test.describe('Vat Manager', () => { ) .toBeTruthy(); }); + + test('should handle config template selection', async () => { + // Get initial config textarea content + const configTextarea = popupPage.locator('[data-testid="config-textarea"]'); + await expect(configTextarea).toBeVisible(); + const initialConfig = await configTextarea.inputValue(); + // Select minimal config template + const configSelect = popupPage.locator('[data-testid="config-select"]'); + await configSelect.selectOption('Minimal'); + // Verify config textarea was updated with minimal config + const minimalConfig = await configTextarea.inputValue(); + expect(minimalConfig).not.toBe(initialConfig); + expect(JSON.parse(minimalConfig)).toMatchObject(minimalClusterConfig); + // Update and reload with minimal config + await popupPage.click('button:text("Update and Reload")'); + // Verify vat table shows only the main vat + const vatTable = popupPage.locator('[data-testid="vat-table"]'); + await expect(vatTable).toBeVisible(); + await expect(vatTable.locator('tr')).toHaveCount(2); // Header + 1 row + await expect(vatTable).toContainText('MainVat'); + }); }); diff --git a/vitest.config.ts b/vitest.config.ts index 3bf92da73..58469ea75 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -68,10 +68,10 @@ export default defineConfig({ lines: 100, }, 'packages/extension/**': { - statements: 73.63, - functions: 78.97, - branches: 70.86, - lines: 73.67, + statements: 0, + functions: 0, + branches: 0, + lines: 0, }, 'packages/kernel/**': { statements: 48.54, From 6bc30d720735dff40113d93f45945f7aba4d7cdd Mon Sep 17 00:00:00 2001 From: Dimitris Marlagkoutsos Date: Mon, 3 Feb 2025 16:55:00 +0100 Subject: [PATCH 2/4] chore: Add empty vat for minimal config --- packages/extension/src/vats/empty-vat.js | 19 +++++++++++++++++++ .../extension/src/vats/minimal-cluster.json | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 packages/extension/src/vats/empty-vat.js diff --git a/packages/extension/src/vats/empty-vat.js b/packages/extension/src/vats/empty-vat.js new file mode 100644 index 000000000..380e34593 --- /dev/null +++ b/packages/extension/src/vats/empty-vat.js @@ -0,0 +1,19 @@ +import { Far } from '@endo/marshal'; + +/** + * Build function for simple test vat. + * + * @param {unknown} _vatPowers - Special powers granted to this vat (not used here). + * @param {unknown} parameters - Initialization parameters from the vat's config object. + * @param {unknown} _baggage - Root of vat's persistent state (not used here). + * @returns {unknown} The root object for the new vat. + */ +export function buildRootObject(_vatPowers, parameters, _baggage) { + const name = parameters?.name ?? 'anonymous'; + console.log(`buildRootObject "${name}"`); + return Far('root', { + bootstrap() { + console.log(`vat ${name} bootstrap() called`); + }, + }); +} diff --git a/packages/extension/src/vats/minimal-cluster.json b/packages/extension/src/vats/minimal-cluster.json index f072ca387..5a210e028 100644 --- a/packages/extension/src/vats/minimal-cluster.json +++ b/packages/extension/src/vats/minimal-cluster.json @@ -3,9 +3,9 @@ "forceReset": true, "vats": { "main": { - "bundleSpec": "http://localhost:3000/sample-vat.bundle", + "bundleSpec": "http://localhost:3000/empty-vat.bundle", "parameters": { - "name": "MainVat" + "name": "EmptyVat" } } } From 197dc0a303466bf1b3b27fe738eb6a30ba9faeb3 Mon Sep 17 00:00:00 2001 From: Dimitris Marlagkoutsos Date: Mon, 3 Feb 2025 20:26:35 +0100 Subject: [PATCH 3/4] fix e2e test --- packages/extension/test/e2e/vat-manager.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/extension/test/e2e/vat-manager.test.ts b/packages/extension/test/e2e/vat-manager.test.ts index 204a51b2a..165b1fb55 100644 --- a/packages/extension/test/e2e/vat-manager.test.ts +++ b/packages/extension/test/e2e/vat-manager.test.ts @@ -229,6 +229,8 @@ test.describe('Vat Manager', () => { const vatTable = popupPage.locator('[data-testid="vat-table"]'); await expect(vatTable).toBeVisible(); await expect(vatTable.locator('tr')).toHaveCount(2); // Header + 1 row - await expect(vatTable).toContainText('MainVat'); + await expect(vatTable).toContainText( + minimalClusterConfig.vats.main.parameters.name, + ); }); }); From 046500ad4ea06977fd7670054d35add984a5aa65 Mon Sep 17 00:00:00 2001 From: Dimitris Marlagkoutsos Date: Mon, 3 Feb 2025 23:35:11 +0100 Subject: [PATCH 4/4] thresholds --- vitest.config.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vitest.config.ts b/vitest.config.ts index 58469ea75..99d446348 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -68,10 +68,10 @@ export default defineConfig({ lines: 100, }, 'packages/extension/**': { - statements: 0, - functions: 0, - branches: 0, - lines: 0, + statements: 74.07, + functions: 79.39, + branches: 70.86, + lines: 74.11, }, 'packages/kernel/**': { statements: 48.54,