story-211: skip selection screen when CLI path argument provided
When a project path is passed on the command line, skip the project selection screen in the frontend and go straight to the main UI. Squash merge of feature/story-211 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,7 @@ describe("App", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
mockedApi.getCurrentProject.mockResolvedValue(null);
|
||||||
mockedApi.getKnownProjects.mockResolvedValue([]);
|
mockedApi.getKnownProjects.mockResolvedValue([]);
|
||||||
mockedApi.getHomeDirectory.mockResolvedValue("/home/user");
|
mockedApi.getHomeDirectory.mockResolvedValue("/home/user");
|
||||||
mockedApi.listDirectoryAbsolute.mockResolvedValue([]);
|
mockedApi.listDirectoryAbsolute.mockResolvedValue([]);
|
||||||
@@ -71,6 +72,26 @@ describe("App", () => {
|
|||||||
return render(<App />);
|
return render(<App />);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it("calls getCurrentProject() on mount", async () => {
|
||||||
|
await renderApp();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedApi.getCurrentProject).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips selection screen and shows workspace when server already has a project open", async () => {
|
||||||
|
mockedApi.getCurrentProject.mockResolvedValue("/home/user/myproject");
|
||||||
|
|
||||||
|
await renderApp();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.queryByPlaceholderText(/\/path\/to\/project/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("renders the selection screen when no project is open", async () => {
|
it("renders the selection screen when no project is open", async () => {
|
||||||
await renderApp();
|
await renderApp();
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,27 @@ import "./App.css";
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [projectPath, setProjectPath] = React.useState<string | null>(null);
|
const [projectPath, setProjectPath] = React.useState<string | null>(null);
|
||||||
|
const [isCheckingProject, setIsCheckingProject] = React.useState(true);
|
||||||
const [errorMsg, setErrorMsg] = React.useState<string | null>(null);
|
const [errorMsg, setErrorMsg] = React.useState<string | null>(null);
|
||||||
const [pathInput, setPathInput] = React.useState("");
|
const [pathInput, setPathInput] = React.useState("");
|
||||||
const [isOpening, setIsOpening] = React.useState(false);
|
const [isOpening, setIsOpening] = React.useState(false);
|
||||||
const [knownProjects, setKnownProjects] = React.useState<string[]>([]);
|
const [knownProjects, setKnownProjects] = React.useState<string[]>([]);
|
||||||
const [homeDir, setHomeDir] = React.useState<string | null>(null);
|
const [homeDir, setHomeDir] = React.useState<string | null>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
api
|
||||||
|
.getCurrentProject()
|
||||||
|
.then((path) => {
|
||||||
|
if (path) {
|
||||||
|
setProjectPath(path);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => console.error(error))
|
||||||
|
.finally(() => {
|
||||||
|
setIsCheckingProject(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
api
|
api
|
||||||
.getKnownProjects()
|
.getKnownProjects()
|
||||||
@@ -138,6 +153,10 @@ function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCheckingProject) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main
|
<main
|
||||||
className="container"
|
className="container"
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test.describe("App boot smoke test", () => {
|
test.describe("App boot smoke test", () => {
|
||||||
|
test.beforeEach(async ({ request }) => {
|
||||||
|
// Close any project the server may have auto-opened (e.g. via CLI path
|
||||||
|
// argument) so we always start from the selection screen.
|
||||||
|
await request.delete("/api/project").catch(() => {
|
||||||
|
// Ignore errors when no project is open or backend is unavailable.
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("renders the project selection screen", async ({ page }) => {
|
test("renders the project selection screen", async ({ page }) => {
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user