fix(frontend): clarify workspace information
This commit is contained in:
@@ -169,7 +169,7 @@ test("offers a button above the workspace list that returns to Level 1", async (
|
||||
expect(screen.queryByRole("heading", { name: "Workspace-specific actions" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("level one explains the read-only Git sequence and the repository update button", async () => {
|
||||
test("level one explains workspace files, shared repositories, and the read-only Git flow", async () => {
|
||||
const user = userEvent.setup();
|
||||
server.use(http.post("/api/workspace-registry/pull", () => HttpResponse.json({
|
||||
branch: "main", head: "b".repeat(40), ahead: 0, behind: 0, degraded: false,
|
||||
@@ -178,30 +178,56 @@ test("level one explains the read-only Git sequence and the repository update bu
|
||||
|
||||
expect(await screen.findByRole("heading", { name: "Workspace management" })).toBeVisible();
|
||||
const overview = screen.getByTestId("workspace-overview");
|
||||
expect(within(overview).getByText(/Git server such as GitHub, GitLab, or Gitea/i)).toBeVisible();
|
||||
expect(within(overview).getByText(/configured during ThothII installation/i)).toBeVisible();
|
||||
const repositoryStep = within(overview).getAllByRole("listitem")[0];
|
||||
expect(repositoryStep).toHaveTextContent(/create a workspace repository/i);
|
||||
expect(repositoryStep).toHaveTextContent(/one directory for each workspace/i);
|
||||
expect(repositoryStep).toHaveTextContent(/thoth-workspaces\.yaml/i);
|
||||
expect(repositoryStep).toHaveTextContent(/database connection/i);
|
||||
expect(repositoryStep).toHaveTextContent(/Evidence sources/i);
|
||||
expect(repositoryStep).toHaveTextContent(/vector-database collection/i);
|
||||
expect(within(overview).getByRole("heading", { name: "What workspace files tell ThothII" })).toBeVisible();
|
||||
expect(within(overview).getByText(/A workspace file describes one data environment/i)).toBeVisible();
|
||||
expect(within(overview).getByText(/one Git repository can contain many workspaces/i)).toBeVisible();
|
||||
expect(within(overview).getByText(/each workspace has its own directory/i)).toHaveTextContent(/database target/i);
|
||||
expect(within(overview).getByText(/each workspace has its own directory/i)).toHaveTextContent(/Evidence/i);
|
||||
const repositorySteps = within(overview).getByRole("list");
|
||||
expect(repositorySteps).toHaveClass("gap-0");
|
||||
expect(within(repositorySteps).getAllByRole("listitem")).toHaveLength(3);
|
||||
expect(within(overview).getByRole("link", { name: /workspace authoring instructions on GitHub/i })).toHaveAttribute(
|
||||
"href",
|
||||
"https://github.com/mptyl/ThothII/blob/main/docs/install/local-workspace-registry.md#prepare-and-publish-a-workspace-source",
|
||||
);
|
||||
const repositoryLink = await within(overview).findByRole("link", {
|
||||
name: "git.example.test/analytics/thoth-workspaces",
|
||||
});
|
||||
expect(repositoryLink).toHaveAttribute("href", "https://git.example.test/analytics/thoth-workspaces");
|
||||
expect(repositoryLink).toHaveAttribute("target", "_blank");
|
||||
expect(repositoryLink).toHaveAttribute("rel", "noreferrer");
|
||||
expect(within(overview).getByText(/configured during ThothII installation/i)).toBeVisible();
|
||||
expect(within(overview).getAllByText(/managed read-only checkout/i)).toHaveLength(2);
|
||||
expect(within(overview).getByText(/current active revision remains unchanged/i)).toBeVisible();
|
||||
expect(within(overview).getByText(/No workspace selection is required/i)).toBeVisible();
|
||||
expect(await within(overview).findByText("git.example.test/analytics/thoth-workspaces")).toBeVisible();
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Update workspace repository" }));
|
||||
expect(await screen.findByText("Workspace repository updated and validated.")).toBeVisible();
|
||||
expect(screen.getByText(/create a workspace repository/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/A workspace file describes one data environment/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/create a workspace repository/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/import|export|bundle/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("shows a local workspace repository as text instead of an invalid web link", async () => {
|
||||
server.use(http.get("/api/workspace-registry/status", () => HttpResponse.json({
|
||||
branch: "main",
|
||||
head: "a".repeat(40),
|
||||
ahead: 0,
|
||||
behind: 0,
|
||||
degraded: false,
|
||||
repository: {
|
||||
host: "local",
|
||||
repository: "configured-repository",
|
||||
transport: "local",
|
||||
},
|
||||
})));
|
||||
renderManager();
|
||||
|
||||
const overview = await screen.findByTestId("workspace-overview");
|
||||
expect(await within(overview).findByText("local/configured-repository")).toBeVisible();
|
||||
expect(within(overview).queryByRole("link", { name: "local/configured-repository" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("workspace-specific commands remain isolated until a workspace is selected", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderManager();
|
||||
@@ -209,10 +235,15 @@ test("workspace-specific commands remain isolated until a workspace is selected"
|
||||
expect(screen.queryByRole("heading", { name: "Workspace-specific actions" })).not.toBeInTheDocument();
|
||||
await user.click(await screen.findByRole("button", { name: "PSD Clinical" }));
|
||||
|
||||
expect(await screen.findByRole("heading", { name: "About this workspace" })).toBeVisible();
|
||||
expect(await screen.findByRole("heading", { name: "Workspace-specific actions" })).toBeVisible();
|
||||
expect(screen.getByText(/reads this revision without modifying or publishing it/i)).toBeVisible();
|
||||
expect(screen.getByText(/checks workspace.yaml and the required workspace directories/i)).toBeVisible();
|
||||
expect(screen.getByText(/temporary decrypted credentials/i)).toBeVisible();
|
||||
expect(screen.getByText(/one workspace in the shared repository/i)).toHaveTextContent(/workspace.yaml/i);
|
||||
expect(screen.getByText(/one workspace in the shared repository/i)).toHaveTextContent(/Evidence/i);
|
||||
expect(screen.getByText(/exact version currently activated by ThothII/i)).toBeVisible();
|
||||
expect(screen.getByText(/does not modify the repository/i)).toBeVisible();
|
||||
expect(screen.getByText(/confirms that workspace.yaml and the directories/i)).toBeVisible();
|
||||
expect(screen.getByText(/reach the data warehouse and Evidence sources/i)).toBeVisible();
|
||||
expect(screen.getByText(/credentials only for the duration of the test/i)).toBeVisible();
|
||||
const databaseField = screen.getByText("Database").parentElement;
|
||||
expect(databaseField).not.toBeNull();
|
||||
expect(databaseField).toHaveTextContent("engine: postgres");
|
||||
|
||||
@@ -386,6 +386,9 @@ export function WorkspaceManager({
|
||||
const repositoryLabel = repository
|
||||
? `${repository.host}/${repository.repository}`
|
||||
: "the repository configured for this ThothII installation";
|
||||
const repositoryWebUrl = repository && repository.transport !== "local"
|
||||
? `https://${repository.host}/${repository.repository}`
|
||||
: undefined;
|
||||
const runtime = runtimeQuery.data;
|
||||
const hasEnteredSecrets = Object.values(secretValues).some((value) => value.length > 0);
|
||||
|
||||
@@ -459,16 +462,34 @@ export function WorkspaceManager({
|
||||
<section data-testid="workspace-overview" className="mx-auto max-w-3xl space-y-5">
|
||||
<div>
|
||||
<p className="thot-label">Level 1 · Repository</p>
|
||||
<h2 className="font-heading text-xl font-semibold">How workspaces reach ThothII</h2>
|
||||
<h2 className="font-heading text-xl font-semibold">What workspace files tell ThothII</h2>
|
||||
</div>
|
||||
<div className="grid gap-3 text-sm leading-6 text-muted-foreground">
|
||||
<p>
|
||||
A workspace file describes one data environment. Its <code>workspace.yaml</code> gives ThothII the workspace identity and language, the database target and schema it may query, the Evidence sources that support analysis, and the processing settings to apply. Runtime credentials are stored separately from Git.
|
||||
</p>
|
||||
<p>
|
||||
One Git repository can contain many workspaces. At its root, <code>thoth-workspaces.yaml</code> lists them, and each workspace has its own directory, <code>workspace.yaml</code>, database target and access method, and Evidence sources. This keeps separate data environments and supporting material distinct while versioning them together.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-heading font-semibold text-foreground">How this installation uses the repository</h3>
|
||||
<ol className="mt-1 grid list-decimal gap-0 pl-5 text-sm leading-6 text-muted-foreground">
|
||||
<li>
|
||||
The repository address, branch, and read-only Git credentials are configured during ThothII installation. This installation reads{" "}
|
||||
{repositoryWebUrl ? (
|
||||
<a href={repositoryWebUrl} target="_blank" rel="noreferrer" className="break-all font-medium text-foreground underline underline-offset-4 hover:text-primary">{repositoryLabel}</a>
|
||||
) : (
|
||||
<span className="font-medium text-foreground">{repositoryLabel}</span>
|
||||
)}{" "}
|
||||
on branch <span className="font-mono text-foreground">{statusQuery.data?.branch ?? "main"}</span>.
|
||||
</li>
|
||||
<li>ThothII fetches the configured branch into its managed read-only checkout, reads the workspaces listed in the repository, and validates the complete candidate revision before activating it.</li>
|
||||
<li>The active revision stays read-only. ThothII never edits, commits, pushes, or publishes workspace files; if validation fails, the previous valid revision stays active.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<ol className="grid list-decimal gap-3 pl-5 text-sm leading-6 text-muted-foreground">
|
||||
<li><span className="font-medium text-foreground">Create a workspace repository</span> in any local directory you choose. Add one directory for each workspace you want ThothII to manage. At the repository root, <code>thoth-workspaces.yaml</code> lists those workspaces; each workspace directory contains its own <code>workspace.yaml</code>, which declares the database connection, the Evidence sources, and the ThothII vector-database collection used during the process.</li>
|
||||
<li>Publish that source by committing and pushing it to a repository hosted by a Git server such as GitHub, GitLab, or Gitea.</li>
|
||||
<li>The repository address, branch, and read-only Git credentials are configured during ThothII installation. This installation reads <span className="font-medium text-foreground">{repositoryLabel}</span> on branch <span className="font-mono text-foreground">{statusQuery.data?.branch ?? "main"}</span>.</li>
|
||||
<li>ThothII fetches the configured branch into its managed read-only checkout, validates the complete candidate revision, and activates it only when validation succeeds. It never edits, commits, pushes, or publishes workspace source.</li>
|
||||
</ol>
|
||||
<p className="text-sm leading-6 text-muted-foreground">
|
||||
Follow the <a href={workspaceAuthoringGuideUrl} target="_blank" rel="noreferrer" className="font-medium text-foreground underline underline-offset-4 hover:text-primary">workspace authoring instructions on GitHub</a> for the required layout and validation rules.
|
||||
The <a href={workspaceAuthoringGuideUrl} target="_blank" rel="noreferrer" className="font-medium text-foreground underline underline-offset-4 hover:text-primary">workspace authoring instructions on GitHub</a> explain the required layout and validation rules for people who maintain the repository.
|
||||
</p>
|
||||
{canManageWorkspace && <div className="rounded-lg border border-border bg-muted/30 p-4">
|
||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||
@@ -486,7 +507,7 @@ export function WorkspaceManager({
|
||||
You can inspect workspaces. Workspace updates, validation, and connection tests require workspace management permission.
|
||||
</p>
|
||||
)}
|
||||
<p className="text-sm text-muted-foreground">Select a workspace from the left only for workspace-specific validation, runtime credentials, and connection tests.</p>
|
||||
<p className="text-sm leading-6 text-muted-foreground">Selecting a workspace from the left opens its own revision, database and Evidence configuration, runtime requirements, validation, and connection checks.</p>
|
||||
</section>
|
||||
) : (
|
||||
<section className="mx-auto max-w-4xl space-y-5">
|
||||
@@ -503,9 +524,14 @@ export function WorkspaceManager({
|
||||
|
||||
{detailQuery.data && runtime && (
|
||||
<>
|
||||
<div>
|
||||
<h3 className="font-heading text-lg font-semibold">Workspace-specific actions</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">The actions below apply only to <span className="font-medium text-foreground">{selectedSummary.displayName}</span>. ThothII reads this revision without modifying or publishing it.</p>
|
||||
<div className="max-w-3xl">
|
||||
<h3 className="font-heading text-lg font-semibold">About this workspace</h3>
|
||||
<p className="mt-1 text-sm leading-6 text-muted-foreground">
|
||||
<span className="font-medium text-foreground">{selectedSummary.displayName}</span> is one workspace in the shared repository. Its workspace.yaml defines the database target, processing settings, and Evidence sources for this data environment. Other workspaces in the same repository can use different databases and keep their Evidence separate.
|
||||
</p>
|
||||
<p className="mt-2 text-sm leading-6 text-muted-foreground">
|
||||
The active revision shown below is the exact version currently activated by ThothII. Reading or checking it does not modify the repository. Source changes are made in Git and become available after a new revision is fetched, validated, and activated.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<dl className="grid gap-3 rounded-lg border border-border bg-muted/20 p-4 text-sm sm:grid-cols-2">
|
||||
@@ -522,10 +548,15 @@ export function WorkspaceManager({
|
||||
<div><dt className="text-xs text-muted-foreground">Runtime status</dt><dd>{stateLabel(runtime.configurationState)}</dd></div>
|
||||
</dl>
|
||||
|
||||
<div>
|
||||
<h3 className="font-heading text-lg font-semibold">Workspace-specific actions</h3>
|
||||
<p className="mt-1 text-sm leading-6 text-muted-foreground">The validation and connection checks below apply only to <span className="font-medium text-foreground">{selectedSummary.displayName}</span> and read its active revision without changing or publishing it.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 lg:grid-cols-2">
|
||||
<div data-testid="workspace-validation-card" className="rounded-lg border border-border p-4">
|
||||
<h4 className="font-heading font-semibold">Validate workspace source</h4>
|
||||
<p className="mt-1 min-h-12 text-sm leading-5 text-muted-foreground">Checks workspace.yaml and the required workspace directories against the supported workspace schema. No source file is changed.</p>
|
||||
<p className="mt-1 min-h-12 text-sm leading-5 text-muted-foreground">Confirms that workspace.yaml and the directories belonging to this workspace follow the supported format. This identifies source problems before activation, without changing repository files.</p>
|
||||
{validationNotice && (
|
||||
<p role="status" className="mt-3 rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700">
|
||||
<CheckCircle2 className="mr-1 inline size-4 text-emerald-600" />{validationNotice}
|
||||
@@ -546,7 +577,7 @@ export function WorkspaceManager({
|
||||
</div>
|
||||
<div data-testid="workspace-connection-card" className="rounded-lg border border-border p-4">
|
||||
<h4 className="font-heading font-semibold">Test workspace connections</h4>
|
||||
<p className="mt-1 min-h-12 text-sm leading-5 text-muted-foreground">Uses temporary decrypted credentials to verify the configured data warehouse and Evidence source. Temporary files are deleted after the test.</p>
|
||||
<p className="mt-1 min-h-12 text-sm leading-5 text-muted-foreground">Verifies that ThothII can reach the data warehouse and Evidence sources configured for this workspace. It decrypts credentials only for the duration of the test and deletes temporary files when the check finishes.</p>
|
||||
{connectionNotice && (
|
||||
<p role="status" className="mt-3 rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700">
|
||||
<CheckCircle2 className="mr-1 inline size-4 text-emerald-600" />{connectionNotice}
|
||||
@@ -589,7 +620,7 @@ export function WorkspaceManager({
|
||||
<KeyRound className="mt-0.5 size-5 text-primary" />
|
||||
<div>
|
||||
<h3 className="font-heading font-semibold">Runtime secrets</h3>
|
||||
<p className="mt-1 text-sm leading-5 text-muted-foreground">Enter only new or replacement values. Stored values are never displayed. Saving replaces the selected secret and clears the form field.</p>
|
||||
<p className="mt-1 text-sm leading-5 text-muted-foreground">Some connectors need credentials at runtime. ThothII stores these values separately from Git and never displays stored values. Enter a value only when adding or replacing a credential for this workspace.</p>
|
||||
</div>
|
||||
</div>
|
||||
{runtime.requirements.length === 0 ? (
|
||||
|
||||
Reference in New Issue
Block a user