From 1e7c9a5f400554a22dd7cc09917e5eff35bd8ccb Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Mon, 24 Aug 2026 15:14:52 -0400 Subject: [PATCH 1/9] feat(home): offer VS Code alongside Docker in the quick start DocumentDB Local shipped in the VS Code extension's 0.10.0 release: the extension now creates and starts the container itself, so someone already working in VS Code no longer needs to run Docker by hand and then type a port, username, password and TLS choice back into a connection wizard. The home page only offered the Docker command, so that path was invisible to the people it was built for -- the ones who arrive at the site without the extension and leave with a terminal command. Docker stays selected by default. It works everywhere and needs nothing beyond Docker itself, while the VS Code path only pays off for people who already live in that editor, so it is offered rather than assumed. The install link comes before the deep link, and neither appears alone. A vscode:// URL for an extension that is not installed does nothing visible at all -- no error, no navigation -- so presenting it on its own would leave a first-time visitor clicking a button that silently does nothing. The heading moves from 'Run locally with Docker' to 'Run DocumentDB locally', since it now covers both. The run-with-docker anchor is kept: nothing in the repository links to it, but it is a public URL and it still lands on the right card. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Guanzhou Song --- app/components/QuickStartTabs.tsx | 159 ++++++++++++++++++++++++++++++ app/page.tsx | 56 ++++++----- app/services/externalLinks.ts | 14 +++ 3 files changed, 207 insertions(+), 22 deletions(-) create mode 100644 app/components/QuickStartTabs.tsx diff --git a/app/components/QuickStartTabs.tsx b/app/components/QuickStartTabs.tsx new file mode 100644 index 0000000..bf0a1df --- /dev/null +++ b/app/components/QuickStartTabs.tsx @@ -0,0 +1,159 @@ +"use client"; + +import Link from "next/link"; +import { useRef, useState } from "react"; +import CommandSnippet from "./CommandSnippet"; + +export type QuickStartStep = { + step: string; + description: string; +}; + +type QuickStartTabsProps = { + dockerCommand: string; + dockerSteps: QuickStartStep[]; + vscodeSteps: QuickStartStep[]; + /** Deep link that opens the extension's DocumentDB Local setup wizard. */ + vscodeDeepLinkUrl: string; + /** Marketplace page, for visitors who do not have the extension yet. */ + vscodeMarketplaceUrl: string; +}; + +const TABS = [ + { id: "docker", label: "Docker" }, + { id: "vscode", label: "VS Code" }, +] as const; + +type TabId = (typeof TABS)[number]["id"]; + +function StepList({ steps }: { steps: QuickStartStep[] }) { + return ( +
    + {steps.map((item) => ( +
  1. + + {item.step} + +

    {item.description}

    +
  2. + ))} +
+ ); +} + +/** + * The home page quick start, offering the two ways to get a local DocumentDB running. + * + * Docker stays first because it is the path that works everywhere and needs nothing installed + * beyond Docker itself. The VS Code path is newer and shorter — the extension provisions the + * container itself — but only pays off for people who already work in VS Code, so it is offered + * rather than assumed. + */ +export default function QuickStartTabs({ + dockerCommand, + dockerSteps, + vscodeSteps, + vscodeDeepLinkUrl, + vscodeMarketplaceUrl, +}: QuickStartTabsProps) { + const [activeTab, setActiveTab] = useState("docker"); + const tabRefs = useRef>({}); + + // Arrow keys move between tabs, which is what a tablist is expected to do; without it the + // only way through is Tab, and that leaves the panel. + const onTabKeyDown = (event: React.KeyboardEvent) => { + if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") { + return; + } + + event.preventDefault(); + const currentIndex = TABS.findIndex((tab) => tab.id === activeTab); + const delta = event.key === "ArrowRight" ? 1 : -1; + const next = TABS[(currentIndex + delta + TABS.length) % TABS.length]; + + setActiveTab(next.id); + tabRefs.current[next.id]?.focus(); + }; + + return ( +
+
+ {TABS.map((tab) => { + const isActive = tab.id === activeTab; + + return ( + + ); + })} +
+ + + + +
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index a8a7098..54306ea 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,11 @@ import Image from "next/image"; import Link from "next/link"; -import CommandSnippet from "./components/CommandSnippet"; -import { documentdbKubernetesOperatorQuickStartUrl } from "./services/externalLinks"; +import QuickStartTabs from "./components/QuickStartTabs"; +import { + documentdbKubernetesOperatorQuickStartUrl, + documentdbVsCodeExtensionMarketplaceUrl, + documentdbVsCodeLocalQuickStartDeepLink, +} from "./services/externalLinks"; import { getMetadata } from "./services/metadataService"; import { documentdbGitHubForks, @@ -39,7 +43,7 @@ const quickRunCommand = `docker run -dt --name documentdb \\ --username \\ --password `; -const quickStartSteps = [ +const dockerQuickStartSteps = [ { step: "01", description: "Run DocumentDB Local with Docker.", @@ -54,6 +58,23 @@ const quickStartSteps = [ }, ]; +const vscodeQuickStartSteps = [ + { + step: "01", + description: "Install the DocumentDB extension for Visual Studio Code.", + }, + { + step: "02", + description: + "Open the DocumentDB Local setup and let the extension create and start the container for you.", + }, + { + step: "03", + description: + "Browse databases, run queries, and edit documents without leaving the editor.", + }, +]; + const kubernetesOperatorEntryPoints = [ { title: "Local clusters", @@ -372,29 +393,20 @@ export default function Home() { Quick start

- Run locally with Docker + Run DocumentDB locally

- Start DocumentDB Local with Docker, then connect on port - 10260. + Start DocumentDB Local with Docker, or let the VS Code + extension set it up for you.

- -
    - {quickStartSteps.map((item) => ( -
  1. - - {item.step} - -

    - {item.description} -

    -
  2. - ))} -
+
Date: Thu, 10 Sep 2026 10:16:21 -0400 Subject: [PATCH 2/9] Refresh VS Code quick start for released local setup Require extension 0.10.1 or later, explain the Docker prerequisite and wizard actions, and provide the Command Palette fallback. The /local deep link shipped in microsoft/vscode-documentdb#898 and no longer needs a release blocker. Preserve the Docker default and add rendered-homepage regression coverage. Signed-off-by: Guanzhou Song Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- app/components/QuickStartTabs.tsx | 24 +++++------ app/page.tsx | 7 ++-- app/services/externalLinks.ts | 8 ---- tests/quickStart.test.ts | 69 +++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 tests/quickStart.test.ts diff --git a/app/components/QuickStartTabs.tsx b/app/components/QuickStartTabs.tsx index bf0a1df..a01b37a 100644 --- a/app/components/QuickStartTabs.tsx +++ b/app/components/QuickStartTabs.tsx @@ -44,14 +44,6 @@ function StepList({ steps }: { steps: QuickStartStep[] }) { ); } -/** - * The home page quick start, offering the two ways to get a local DocumentDB running. - * - * Docker stays first because it is the path that works everywhere and needs nothing installed - * beyond Docker itself. The VS Code path is newer and shorter — the extension provisions the - * container itself — but only pays off for people who already work in VS Code, so it is offered - * rather than assumed. - */ export default function QuickStartTabs({ dockerCommand, dockerSteps, @@ -131,6 +123,10 @@ export default function QuickStartTabs({ aria-labelledby="quickstart-tab-vscode" hidden={activeTab !== "vscode"} > +

+ Requires Docker Engine or Docker Desktop running Linux containers in + your VS Code environment. +

Get the extension - {/* - * A `vscode://` link does nothing at all when VS Code is not installed — no error, no - * navigation — so it is offered second and never on its own. Someone arriving without - * the extension gets the install link first and this becomes the obvious next step. - */}
+

+ If the link does not open setup, run{" "} + + DocumentDB: Set up DocumentDB Local + {" "} + from the VS Code Command Palette. +

); diff --git a/app/page.tsx b/app/page.tsx index 54306ea..389567e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -61,17 +61,18 @@ const dockerQuickStartSteps = [ const vscodeQuickStartSteps = [ { step: "01", - description: "Install the DocumentDB extension for Visual Studio Code.", + description: + "Install or update DocumentDB for VS Code to version 0.10.1 or later.", }, { step: "02", description: - "Open the DocumentDB Local setup and let the extension create and start the container for you.", + "Open setup, confirm if prompted, select Continue, then review the defaults and select Start DocumentDB Local.", }, { step: "03", description: - "Browse databases, run queries, and edit documents without leaving the editor.", + "When setup finishes, select Open Connection to browse data and run queries.", }, ]; diff --git a/app/services/externalLinks.ts b/app/services/externalLinks.ts index 645203b..7a3ada9 100644 --- a/app/services/externalLinks.ts +++ b/app/services/externalLinks.ts @@ -17,13 +17,5 @@ export const documentdbKubernetesOperatorGitHubUrl = export const documentdbVsCodeExtensionMarketplaceUrl = 'https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-documentdb'; -// Deep link into the extension's DocumentDB Local setup wizard. -// -// The path names the action; an empty path means "connect", which is what every link published -// before the extension supported actions relies on. See the extension's -// docs/user-manual/how-to-construct-url.md for the full vocabulary. -// -// Requires the extension to be installed: a `vscode://` URL for an absent extension does nothing -// visible at all, so never present this without the marketplace link beside it. export const documentdbVsCodeLocalQuickStartDeepLink = 'vscode://ms-azuretools.vscode-documentdb/local'; diff --git a/tests/quickStart.test.ts b/tests/quickStart.test.ts new file mode 100644 index 0000000..525f278 --- /dev/null +++ b/tests/quickStart.test.ts @@ -0,0 +1,69 @@ +import { createElement } from 'react'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { describe, expect, it } from 'vitest'; +import Home from '../app/page'; +import { + documentdbVsCodeExtensionMarketplaceUrl, + documentdbVsCodeLocalQuickStartDeepLink, +} from '../app/services/externalLinks'; + +const html = renderToStaticMarkup(createElement(Home)); + +describe('homepage local quick start', () => { + it('preserves the public anchor and Docker as the default tab', () => { + expect(html).toContain('id="run-with-docker"'); + expect(html).toMatch( + /id="quickstart-tab-docker" aria-selected="true" aria-controls="quickstart-panel-docker" tabindex="0"/, + ); + expect(html).toMatch( + /id="quickstart-tab-vscode" aria-selected="false" aria-controls="quickstart-panel-vscode" tabindex="-1"/, + ); + expect(html).toMatch( + /id="quickstart-panel-docker" aria-labelledby="quickstart-tab-docker">/, + ); + expect(html).toMatch( + /id="quickstart-panel-vscode" aria-labelledby="quickstart-tab-vscode" hidden=""/, + ); + expect(html).toContain('docker run -dt --name documentdb'); + expect(html).toContain('-p 10260:10260'); + expect(html).toContain( + 'ghcr.io/documentdb/documentdb/documentdb-local:latest', + ); + }); + + it('offers the marketplace before the supported local setup deep link', () => { + expect(documentdbVsCodeExtensionMarketplaceUrl).toBe( + 'https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-documentdb', + ); + expect(documentdbVsCodeLocalQuickStartDeepLink).toBe( + 'vscode://ms-azuretools.vscode-documentdb/local', + ); + const marketplace = html.indexOf( + `href="${documentdbVsCodeExtensionMarketplaceUrl}"`, + ); + const setup = html.indexOf( + `href="${documentdbVsCodeLocalQuickStartDeepLink}"`, + ); + expect(marketplace).toBeGreaterThan(-1); + expect(setup).toBeGreaterThan(marketplace); + }); + + it('states the shipped minimum version and Docker prerequisite', () => { + expect(html).toContain('version 0.10.1 or later'); + expect(html).toContain( + 'Requires Docker Engine or Docker Desktop running Linux containers in your VS Code environment.', + ); + }); + + it('describes the setup actions and provides a Command Palette fallback', () => { + expect(html).toContain( + 'Open setup, confirm if prompted, select Continue, then review the defaults and select Start DocumentDB Local.', + ); + expect(html).toContain( + 'When setup finishes, select Open Connection to browse data and run queries.', + ); + expect(html).toContain('If the link does not open setup, run '); + expect(html).toContain('DocumentDB: Set up DocumentDB Local'); + expect(html).toContain('from the VS Code Command Palette.'); + }); +}); From 95c246aaedc9650bba5f001cbd5cfb0d6932cb57 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 10 Sep 2026 10:19:32 -0400 Subject: [PATCH 3/9] Enable JSX rendering in homepage regression tests Use Vite's automatic JSX runtime for Vitest without changing Next.js's preserve setting or adding dependencies. Signed-off-by: Guanzhou Song Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b2b0079f-68de-4316-8592-64ccfa495ee3 --- vitest.config.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 vitest.config.ts diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..21215b0 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + oxc: { + jsx: { + runtime: 'automatic', + }, + }, +}); From cb4c620998f457aaef76fd610cf225869d333669 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 10 Sep 2026 10:29:48 -0400 Subject: [PATCH 4/9] Name the link confirmation button in the VS Code quick start The setup step said "confirm if prompted" without naming what to confirm. The extension's deep-link handler shows a modal whose confirm button is labeled "Open setup", so name it, matching how the remaining steps already name Continue, Start DocumentDB Local, and Open Connection. Verified against microsoft/vscode-documentdb main (013e429f): src/vscodeUriHandler.ts shows the confirmation, and LocalQuickStart.tsx drives introduction -> Continue -> configure -> Start DocumentDB Local -> Open Connection. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017dU6YABKoUxKLU2AB1qeH1 --- app/page.tsx | 2 +- tests/quickStart.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 389567e..b29a5c7 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -67,7 +67,7 @@ const vscodeQuickStartSteps = [ { step: "02", description: - "Open setup, confirm if prompted, select Continue, then review the defaults and select Start DocumentDB Local.", + "Select Open setup when VS Code confirms the link, then Continue, review the defaults, and select Start DocumentDB Local.", }, { step: "03", diff --git a/tests/quickStart.test.ts b/tests/quickStart.test.ts index 525f278..1a468fe 100644 --- a/tests/quickStart.test.ts +++ b/tests/quickStart.test.ts @@ -57,7 +57,7 @@ describe('homepage local quick start', () => { it('describes the setup actions and provides a Command Palette fallback', () => { expect(html).toContain( - 'Open setup, confirm if prompted, select Continue, then review the defaults and select Start DocumentDB Local.', + 'Select Open setup when VS Code confirms the link, then Continue, review the defaults, and select Start DocumentDB Local.', ); expect(html).toContain( 'When setup finishes, select Open Connection to browse data and run queries.', From 0d5658024c18692806fee6fd742b1d992f046a17 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 10 Sep 2026 10:47:34 -0400 Subject: [PATCH 5/9] Make the quick start readable to someone who has not seen the wizard A design and product review of the tabbed quick start found the VS Code panel narrated a wizard the reader has never seen, and led with a deep link that silently does nothing for the very person the tab is for. Copy: - The prerequisite now says the VS Code path still uses Docker, and that the extension never installs Docker or changes the user's system, rather than the jargon "in your VS Code environment". - Step 01 names what gets installed instead of pinning extension version 0.10.1, which is maintenance debt on a homepage and is noise for a new installer who gets the latest anyway. - Step 02 was a four-action transcript of wizard buttons; it now says click, allow, and follow the wizard. - Step 03 states the outcome, port and generated credentials, so the reader knows what they got. - The fallback names the likely cause instead of treating "no VS Code", "no extension", and "old extension" as the same thing. - The Docker steps drop the one that restated the command above it, add a first-run time expectation, and end on a concrete connect-and-query instead of "the setup you need". Structure: - Tabs are Terminal and VS Code. Labelling one "Docker" implied the other avoided Docker, when both start the same container. - Install the extension is now the primary action; the deep link is secondary, matching the order of the steps. - The card footer link follows the active tab and points at that path's full guide. Download packages is gone, since the hero already has Download pointing at the same page. - Each panel links to the other path so neither is hidden behind a tab. Presentation: - The tablist gets a solid active state and larger hit targets, so it reads as a control rather than as another badge between the Quick start chip and the numbered step markers. - The hero grid is items-start. It was items-center, and since the card is the taller column, switching tabs moved the DocumentDB headline. - Home and End keys move between tabs, per the ARIA authoring practices. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017dU6YABKoUxKLU2AB1qeH1 --- app/components/QuickStartTabs.tsx | 105 ++++++++++++++++++++++-------- app/page.tsx | 37 ++++------- tests/quickStart.test.ts | 53 +++++++++++---- 3 files changed, 132 insertions(+), 63 deletions(-) diff --git a/app/components/QuickStartTabs.tsx b/app/components/QuickStartTabs.tsx index a01b37a..26af5cb 100644 --- a/app/components/QuickStartTabs.tsx +++ b/app/components/QuickStartTabs.tsx @@ -17,10 +17,15 @@ type QuickStartTabsProps = { vscodeDeepLinkUrl: string; /** Marketplace page, for visitors who do not have the extension yet. */ vscodeMarketplaceUrl: string; + /** Full guide for each path, linked from the footer of the matching panel. */ + dockerDocsUrl: string; + vscodeDocsUrl: string; }; +// "Terminal" rather than "Docker": both paths run the same Docker container, and labelling one +// of them "Docker" implies the other avoids it. const TABS = [ - { id: "docker", label: "Docker" }, + { id: "terminal", label: "Terminal" }, { id: "vscode", label: "VS Code" }, ] as const; @@ -32,9 +37,9 @@ function StepList({ steps }: { steps: QuickStartStep[] }) { {steps.map((item) => (
  • - + {item.step}

    {item.description}

    @@ -50,24 +55,42 @@ export default function QuickStartTabs({ vscodeSteps, vscodeDeepLinkUrl, vscodeMarketplaceUrl, + dockerDocsUrl, + vscodeDocsUrl, }: QuickStartTabsProps) { - const [activeTab, setActiveTab] = useState("docker"); + const [activeTab, setActiveTab] = useState("terminal"); const tabRefs = useRef>({}); + const selectTab = (id: TabId) => { + setActiveTab(id); + tabRefs.current[id]?.focus(); + }; + // Arrow keys move between tabs, which is what a tablist is expected to do; without it the - // only way through is Tab, and that leaves the panel. + // only way through is Tab, and that leaves the panel. Home/End jump to the ends, per the + // ARIA authoring practices for tabs. const onTabKeyDown = (event: React.KeyboardEvent) => { - if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") { - return; - } - - event.preventDefault(); const currentIndex = TABS.findIndex((tab) => tab.id === activeTab); - const delta = event.key === "ArrowRight" ? 1 : -1; - const next = TABS[(currentIndex + delta + TABS.length) % TABS.length]; - setActiveTab(next.id); - tabRefs.current[next.id]?.focus(); + switch (event.key) { + case "ArrowRight": + case "ArrowLeft": { + event.preventDefault(); + const delta = event.key === "ArrowRight" ? 1 : -1; + selectTab(TABS[(currentIndex + delta + TABS.length) % TABS.length].id); + break; + } + case "Home": + event.preventDefault(); + selectTab(TABS[0].id); + break; + case "End": + event.preventDefault(); + selectTab(TABS[TABS.length - 1].id); + break; + default: + break; + } }; return ( @@ -75,7 +98,7 @@ export default function QuickStartTabs({
    {TABS.map((tab) => { const isActive = tab.id === activeTab; @@ -95,9 +118,11 @@ export default function QuickStartTabs({ tabIndex={isActive ? 0 : -1} onClick={() => setActiveTab(tab.id)} onKeyDown={onTabKeyDown} - className={`rounded-full px-4 py-1.5 text-xs font-semibold transition-colors ${ + // Solid active state so the tablist reads as a control rather than as another + // badge next to the "Quick start" chip and the numbered step markers. + className={`flex-1 rounded-full px-5 py-3 text-sm font-semibold transition-colors sm:flex-none ${ isActive - ? "bg-blue-500/20 text-blue-100" + ? "bg-blue-500 text-white" : "text-gray-400 hover:text-gray-200" }`} > @@ -109,12 +134,23 @@ export default function QuickStartTabs({ + +
    + + {activeTab === "vscode" + ? "Full VS Code guide" + : "Full Docker guide"} + +
    ); } diff --git a/app/page.tsx b/app/page.tsx index b29a5c7..2d301d8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -46,15 +46,18 @@ const quickRunCommand = `docker run -dt --name documentdb \\ const dockerQuickStartSteps = [ { step: "01", - description: "Run DocumentDB Local with Docker.", + description: + "Run the command above. Docker pulls the image on the first run, so give it about a minute.", }, { step: "02", - description: "Connect on port 10260 with your app, shell, or client.", + description: + "Connect on port 10260 with mongosh, any MongoDB driver, or your app.", }, { step: "03", - description: "Continue with the docs or Linux packages for the setup you need.", + description: + "Run your first query. The Docker guide has connection strings and sample data.", }, ]; @@ -62,17 +65,17 @@ const vscodeQuickStartSteps = [ { step: "01", description: - "Install or update DocumentDB for VS Code to version 0.10.1 or later.", + "Install the free DocumentDB for VS Code extension from the Marketplace.", }, { step: "02", description: - "Select Open setup when VS Code confirms the link, then Continue, review the defaults, and select Start DocumentDB Local.", + "Select Open setup in VS Code and allow it to open the link. The wizard starts DocumentDB Local with defaults you can review.", }, { step: "03", description: - "When setup finishes, select Open Connection to browse data and run queries.", + "You get a container on port 10260 with generated credentials. Select Open Connection to browse data and run your first query.", }, ]; @@ -338,7 +341,7 @@ export default function Home() {
    -
    +

    Open source document database @@ -397,8 +400,8 @@ export default function Home() { Run DocumentDB locally

    - Start DocumentDB Local with Docker, or let the VS Code - extension set it up for you. + Run it from your terminal, or let the VS Code extension set it + up for you. Both start the same DocumentDB Local container.

    -
    - - Docker quick start - - - Download packages - -
    diff --git a/tests/quickStart.test.ts b/tests/quickStart.test.ts index 1a468fe..b7d9b09 100644 --- a/tests/quickStart.test.ts +++ b/tests/quickStart.test.ts @@ -10,16 +10,16 @@ import { const html = renderToStaticMarkup(createElement(Home)); describe('homepage local quick start', () => { - it('preserves the public anchor and Docker as the default tab', () => { + it('preserves the public anchor and the terminal path as the default tab', () => { expect(html).toContain('id="run-with-docker"'); expect(html).toMatch( - /id="quickstart-tab-docker" aria-selected="true" aria-controls="quickstart-panel-docker" tabindex="0"/, + /id="quickstart-tab-terminal" aria-selected="true" aria-controls="quickstart-panel-terminal" tabindex="0"/, ); expect(html).toMatch( /id="quickstart-tab-vscode" aria-selected="false" aria-controls="quickstart-panel-vscode" tabindex="-1"/, ); expect(html).toMatch( - /id="quickstart-panel-docker" aria-labelledby="quickstart-tab-docker">/, + /id="quickstart-panel-terminal" aria-labelledby="quickstart-tab-terminal">/, ); expect(html).toMatch( /id="quickstart-panel-vscode" aria-labelledby="quickstart-tab-vscode" hidden=""/, @@ -31,7 +31,15 @@ describe('homepage local quick start', () => { ); }); - it('offers the marketplace before the supported local setup deep link', () => { + it('labels the tabs by interface rather than implying one path avoids Docker', () => { + expect(html).toContain('>Terminal'); + expect(html).toContain('>VS Code'); + expect(html).toContain( + 'Both start the same DocumentDB Local container.', + ); + }); + + it('leads with installing the extension, then opening setup', () => { expect(documentdbVsCodeExtensionMarketplaceUrl).toBe( 'https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-documentdb', ); @@ -46,24 +54,45 @@ describe('homepage local quick start', () => { ); expect(marketplace).toBeGreaterThan(-1); expect(setup).toBeGreaterThan(marketplace); + expect(html).toContain('Install the extension'); + expect(html).toContain('Open setup in VS Code'); + }); + + it('states that the VS Code path still needs Docker and changes nothing else', () => { + expect(html).toContain( + 'Needs Docker Desktop or Docker Engine on the same machine as VS Code.', + ); + expect(html).toContain('It never installs Docker or changes your system.'); }); - it('states the shipped minimum version and Docker prerequisite', () => { - expect(html).toContain('version 0.10.1 or later'); + it('describes the setup outcome without pinning an extension version', () => { + expect(html).toContain( + 'Install the free DocumentDB for VS Code extension from the Marketplace.', + ); expect(html).toContain( - 'Requires Docker Engine or Docker Desktop running Linux containers in your VS Code environment.', + 'The wizard starts DocumentDB Local with defaults you can review.', ); + expect(html).toContain( + 'You get a container on port 10260 with generated credentials.', + ); + // A pinned minimum version on the homepage rots; the guide carries it instead. + expect(html).not.toContain('0.10.1'); }); - it('describes the setup actions and provides a Command Palette fallback', () => { + it('gives each path a concrete first query and a full guide', () => { expect(html).toContain( - 'Select Open setup when VS Code confirms the link, then Continue, review the defaults, and select Start DocumentDB Local.', + 'Connect on port 10260 with mongosh, any MongoDB driver, or your app.', ); + expect(html).toContain('Run your first query.'); + expect(html).toContain('href="/docs/getting-started/docker"'); + expect(html).toContain('Full Docker guide'); + }); + + it('provides a Command Palette fallback that names the likely cause', () => { expect(html).toContain( - 'When setup finishes, select Open Connection to browse data and run queries.', + 'If nothing happens, check that the extension is installed and up to', ); - expect(html).toContain('If the link does not open setup, run '); expect(html).toContain('DocumentDB: Set up DocumentDB Local'); - expect(html).toContain('from the VS Code Command Palette.'); + expect(html).toContain('from the Command Palette.'); }); }); From 33f488703c71fe25996cca056f3431d690c29d3d Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 10 Sep 2026 11:43:36 -0400 Subject: [PATCH 6/9] Fix the guide the VS Code tab opens, and make the hero command runnable Two reviews of the tabbed quick start, one on design and one on correctness, found two defects that reached users. The VS Code guide still described the manual flow. articleService short-circuits on `vscode-quickstart` and returns an inline constant before reading the cloned markdown, so rewriting the guide upstream in documentdb/docs never reached the site. The card told people the extension sets things up for them; one click on "Full VS Code guide" told them to run Docker by hand and type the port and credentials. The inline copy now leads with the wizard and keeps the manual flow as an explicit alternative, preserving the loopback binding, quoted placeholders and TLS guidance that copy already got right. The hero command was a shell syntax error. Unquoted and parse as redirections, so `bash -n` and `zsh -n` both reject it, and step 01 had just started telling people to run it. The placeholders are quoted and the step says to replace them first. The command also published on every interface. The Docker guide it links to says a bare `-p 10260:10260` "publishes it on every interface, which is rarely what you want on a laptop", the VS Code guide uses loopback, and the extension binds hostIp 127.0.0.1. The homepage was the only surface in the product disagreeing, and a test pinned it that way. Copy corrections, each against extension source: - "Both start the same DocumentDB Local container" was false. The paths share an image; the containers, volumes, credentials and seeding all differ. - "never installs Docker or changes your system" overpromised: it creates a container and a persistent volume. The extension's own wording is "nothing else on your machine is changed". - Port 10260 was stated as a guarantee. suggestPort scans forward when it is taken. - Step 02 implied opening the link starts the container. Nothing starts until Continue, then Start DocumentDB Local. - The prerequisite dropped the Linux-containers requirement, which the extension enforces as a hard failure, and "the same machine as VS Code" is wrong for WSL, dev containers and SSH remotes. - mongosh needs TLS and auth flags, and sample data needs --init-data. Structure and a11y: - Both guide links render inside their own panel. The link was conditional on the active tab, so under output: "export" the VS Code guide had no link from the homepage at all. - The cross-panel switches are labelled for screen readers and go both ways. The terminal one now says the extension is a GUI for the same container, instead of restating the tablist above it. - The active tab no longer uses the same solid blue as the hero's primary button. Tests: assertions are scoped to the card and matched attribute by attribute rather than by JSX prop order, the version guard matches shape instead of one literal, and the VS Code guide link is covered. vitest.config renamed to .mts to silence the CJS/ESM warning. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017dU6YABKoUxKLU2AB1qeH1 --- app/components/QuickStartTabs.tsx | 70 +++++++++----- app/page.tsx | 18 ++-- app/services/articleService.ts | 35 +++++-- tests/quickStart.test.ts | 129 +++++++++++++++----------- vitest.config.ts => vitest.config.mts | 0 5 files changed, 160 insertions(+), 92 deletions(-) rename vitest.config.ts => vitest.config.mts (100%) diff --git a/app/components/QuickStartTabs.tsx b/app/components/QuickStartTabs.tsx index 26af5cb..0b4b5d8 100644 --- a/app/components/QuickStartTabs.tsx +++ b/app/components/QuickStartTabs.tsx @@ -22,8 +22,8 @@ type QuickStartTabsProps = { vscodeDocsUrl: string; }; -// "Terminal" rather than "Docker": both paths run the same Docker container, and labelling one -// of them "Docker" implies the other avoids it. +// "Terminal" rather than "Docker": both paths run the same Docker image, and labelling one of +// them "Docker" implies the other avoids Docker. const TABS = [ { id: "terminal", label: "Terminal" }, { id: "vscode", label: "VS Code" }, @@ -122,7 +122,7 @@ export default function QuickStartTabs({ // badge next to the "Quick start" chip and the numbered step markers. className={`flex-1 rounded-full px-5 py-3 text-sm font-semibold transition-colors sm:flex-none ${ isActive - ? "bg-blue-500 text-white" + ? "bg-neutral-700 text-white" : "text-gray-400 hover:text-gray-200" }`} > @@ -138,19 +138,28 @@ export default function QuickStartTabs({ aria-labelledby="quickstart-tab-terminal" hidden={activeTab !== "terminal"} > - +

    - Prefer to set it up from your editor? Use the{" "} + Want a GUI? The{" "} {" "} - tab. + connects to this container too.

    +
    + + Full Docker guide + +
    - -
    - - {activeTab === "vscode" - ? "Full VS Code guide" - : "Full Docker guide"} - +

    + Prefer to start it yourself? The{" "} + {" "} + tab runs the same image with one command. +

    +
    + + Full VS Code guide + +
    ); diff --git a/app/page.tsx b/app/page.tsx index 2d301d8..c26b8d7 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -38,26 +38,26 @@ type Capability = { }; const quickRunCommand = `docker run -dt --name documentdb \\ - -p 10260:10260 \\ + -p 127.0.0.1:10260:10260 \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password `; + --username '' \\ + --password ''`; const dockerQuickStartSteps = [ { step: "01", description: - "Run the command above. Docker pulls the image on the first run, so give it about a minute.", + "Replace the placeholders with your own username and password, then run the command. The image is around 300 MB, so the first run takes a few minutes.", }, { step: "02", description: - "Connect on port 10260 with mongosh, any MongoDB driver, or your app.", + "Connect on 127.0.0.1:10260 with mongosh (TLS flags are in the guide), a MongoDB driver, or your app.", }, { step: "03", description: - "Run your first query. The Docker guide has connection strings and sample data.", + "Run your first query. The Docker guide has connection strings and a flag for sample data.", }, ]; @@ -70,12 +70,12 @@ const vscodeQuickStartSteps = [ { step: "02", description: - "Select Open setup in VS Code and allow it to open the link. The wizard starts DocumentDB Local with defaults you can review.", + "Select Open setup in VS Code, then confirm both prompts. In the wizard select Continue, review the defaults, and select Start DocumentDB Local.", }, { step: "03", description: - "You get a container on port 10260 with generated credentials. Select Open Connection to browse data and run your first query.", + "You get a container on an available port, 10260 unless it is taken, with generated credentials. Select Open Connection to browse data and run your first query.", }, ]; @@ -401,7 +401,7 @@ export default function Home() {

    Run it from your terminal, or let the VS Code extension set it - up for you. Both start the same DocumentDB Local container. + up for you. Both run the same DocumentDB Local image.

    { it('preserves the public anchor and the terminal path as the default tab', () => { expect(html).toContain('id="run-with-docker"'); - expect(html).toMatch( - /id="quickstart-tab-terminal" aria-selected="true" aria-controls="quickstart-panel-terminal" tabindex="0"/, - ); - expect(html).toMatch( - /id="quickstart-tab-vscode" aria-selected="false" aria-controls="quickstart-panel-vscode" tabindex="-1"/, - ); - expect(html).toMatch( - /id="quickstart-panel-terminal" aria-labelledby="quickstart-tab-terminal">/, - ); - expect(html).toMatch( - /id="quickstart-panel-vscode" aria-labelledby="quickstart-tab-vscode" hidden=""/, - ); - expect(html).toContain('docker run -dt --name documentdb'); - expect(html).toContain('-p 10260:10260'); - expect(html).toContain( + + // Asserted attribute by attribute: matching a fixed attribute sequence would fail on a + // no-op JSX prop reorder while telling us nothing extra. + for (const attr of [ + 'id="quickstart-tab-terminal"', + 'aria-selected="true"', + 'aria-controls="quickstart-panel-terminal"', + 'id="quickstart-panel-terminal"', + 'aria-labelledby="quickstart-tab-terminal"', + 'id="quickstart-tab-vscode"', + 'aria-controls="quickstart-panel-vscode"', + 'id="quickstart-panel-vscode"', + 'aria-labelledby="quickstart-tab-vscode"', + ]) { + expect(card).toContain(attr); + } + + // The VS Code panel is the one hidden on first paint, and its tab is out of the tab order. + expect(card).toMatch(/id="quickstart-panel-vscode"[^>]*hidden=""/); + expect(card).not.toMatch(/id="quickstart-panel-terminal"[^>]*hidden=""/); + }); + + it('ships a command that is safe to publish and actually parses in a shell', () => { + // A bare -p publishes on every interface; the guide this card links to calls that out. + expect(card).toContain('-p 127.0.0.1:10260:10260'); + expect(card).not.toContain('-p 10260:10260 '); + // Unquoted is parsed as a redirection, so the pasted command is a syntax error. + expect(card).toContain("--username '<YOUR_USERNAME>'"); + expect(card).toContain("--password '<YOUR_PASSWORD>'"); + expect(card).toContain( 'ghcr.io/documentdb/documentdb/documentdb-local:latest', ); }); - it('labels the tabs by interface rather than implying one path avoids Docker', () => { - expect(html).toContain('>Terminal'); - expect(html).toContain('>VS Code'); - expect(html).toContain( - 'Both start the same DocumentDB Local container.', - ); + it('labels the tabs by interface and says the paths share an image, not a container', () => { + expect(card).toContain('>Terminal'); + expect(card).toContain('>VS Code'); + expect(card).toContain('Both run the same DocumentDB Local image.'); }); it('leads with installing the extension, then opening setup', () => { @@ -46,53 +62,62 @@ describe('homepage local quick start', () => { expect(documentdbVsCodeLocalQuickStartDeepLink).toBe( 'vscode://ms-azuretools.vscode-documentdb/local', ); - const marketplace = html.indexOf( + const marketplace = card.indexOf( `href="${documentdbVsCodeExtensionMarketplaceUrl}"`, ); - const setup = html.indexOf( + const setup = card.indexOf( `href="${documentdbVsCodeLocalQuickStartDeepLink}"`, ); expect(marketplace).toBeGreaterThan(-1); expect(setup).toBeGreaterThan(marketplace); - expect(html).toContain('Install the extension'); - expect(html).toContain('Open setup in VS Code'); + expect(card).toContain('Install the extension'); + expect(card).toContain('Open setup in VS Code'); }); - it('states that the VS Code path still needs Docker and changes nothing else', () => { - expect(html).toContain( - 'Needs Docker Desktop or Docker Engine on the same machine as VS Code.', - ); - expect(html).toContain('It never installs Docker or changes your system.'); + it('states the real Docker prerequisite without overpromising', () => { + expect(card).toContain('set to Linux'); + expect(card).toContain('running wherever VS Code is'); + // "changes your system" was false: it creates a container and a persistent volume. + expect(card).toContain('changes nothing else on your machine'); }); - it('describes the setup outcome without pinning an extension version', () => { - expect(html).toContain( - 'Install the free DocumentDB for VS Code extension from the Marketplace.', - ); - expect(html).toContain( - 'The wizard starts DocumentDB Local with defaults you can review.', - ); - expect(html).toContain( - 'You get a container on port 10260 with generated credentials.', - ); - // A pinned minimum version on the homepage rots; the guide carries it instead. - expect(html).not.toContain('0.10.1'); + it('names both wizard clicks and does not guarantee a port it may not get', () => { + expect(card).toContain('select Continue'); + expect(card).toContain('select Start DocumentDB Local'); + expect(card).toContain('on an available port, 10260 unless it is taken'); }); - it('gives each path a concrete first query and a full guide', () => { - expect(html).toContain( - 'Connect on port 10260 with mongosh, any MongoDB driver, or your app.', - ); - expect(html).toContain('Run your first query.'); - expect(html).toContain('href="/docs/getting-started/docker"'); - expect(html).toContain('Full Docker guide'); + it('carries no pinned extension version, which the guide owns instead', () => { + // Matched by shape rather than by one literal, so bumping the pin to 0.10.2 is caught + // too. Deliberately not a bare \d+\.\d+\.\d+, which would match the 127.0.0.1 in the + // command and the loopback address in the steps. + expect(card).not.toMatch(/version \d+\.\d+\.\d+/i); + expect(card).not.toMatch(/\d+\.\d+\.\d+ or (later|newer|above)/i); + expect(card).not.toMatch(/\bv\d+\.\d+\.\d+\b/); + }); + + it('links both full guides from inside their own panels', () => { + // Conditionally rendering one link left the VS Code guide out of the exported HTML + // entirely, since the server renders with the terminal tab active. + expect(card).toContain('href="/docs/getting-started/docker"'); + expect(card).toContain('href="/docs/getting-started/vscode-quickstart"'); + expect(card).toContain('Full Docker guide'); + expect(card).toContain('Full VS Code guide'); + + const vscodePanel = card.slice(card.indexOf('id="quickstart-panel-vscode"')); + expect(vscodePanel).toContain('href="/docs/getting-started/vscode-quickstart"'); + }); + + it('offers a labelled route between the two paths', () => { + expect(card).toContain('aria-label="Switch to the VS Code tab"'); + expect(card).toContain('aria-label="Switch to the Terminal tab"'); }); it('provides a Command Palette fallback that names the likely cause', () => { - expect(html).toContain( + expect(card).toContain( 'If nothing happens, check that the extension is installed and up to', ); - expect(html).toContain('DocumentDB: Set up DocumentDB Local'); - expect(html).toContain('from the Command Palette.'); + expect(card).toContain('DocumentDB: Set up DocumentDB Local'); + expect(card).toContain('from the Command Palette.'); }); }); diff --git a/vitest.config.ts b/vitest.config.mts similarity index 100% rename from vitest.config.ts rename to vitest.config.mts From 5a033753dc40f150f26fa2f91f064014ebd6e9f3 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Fri, 11 Sep 2026 09:26:13 -0400 Subject: [PATCH 7/9] Make the VS Code tab the easy path Reading the two quick start tabs side by side, the VS Code one read as the heavier option: a Docker prerequisite paragraph, two buttons, three steps and a troubleshooting paragraph in the happy path. The guided path should look like the short one. - Drop the Docker prerequisite. Both paths need Docker and the Terminal tab does not say so; saying it only here made the guided path look like the one with extra requirements. The guide covers Docker properly. - One button, "Set up in VS Code". VS Code offers to install a missing extension when a vscode:// link targets it and re-handles the link after install, so a separate "Install the extension" action was a step the visitor never has to take. The caption explains the install and links the Marketplace, so nothing is hidden. - Two steps instead of three: the wizard, then the payoff. Sample data is seeded by default (loadSampleData in the extension's quickStartTypes.ts). - Troubleshooting moves out of the happy path into one footer sentence that links to the guide's "Set up DocumentDB Local" section. The anchor is derived with the same kebabCase the renderer uses, so a retitled heading moves the link rather than breaking it. On managed devices whose policy sets ExtensionGalleryServiceUrl, the install-on-link fails with "No extension gallery service configured" when the link is also what starts VS Code: the gallery account check runs before the Microsoft auth provider is up, and the URL handler does not retry. A second click once VS Code is running works. A status line shown only after the button is used says so, keeping the caveat away from people who have not clicked yet, and the guide's troubleshooting names the error and both fixes. --- app/components/QuickStartTabs.tsx | 101 ++++++++++++++++++-------- app/page.tsx | 15 ++-- app/services/articleService.ts | 14 +++- tests/quickStart.test.ts | 115 +++++++++++++++++++++--------- 4 files changed, 170 insertions(+), 75 deletions(-) diff --git a/app/components/QuickStartTabs.tsx b/app/components/QuickStartTabs.tsx index 0b4b5d8..c957ca3 100644 --- a/app/components/QuickStartTabs.tsx +++ b/app/components/QuickStartTabs.tsx @@ -15,10 +15,14 @@ type QuickStartTabsProps = { vscodeSteps: QuickStartStep[]; /** Deep link that opens the extension's DocumentDB Local setup wizard. */ vscodeDeepLinkUrl: string; - /** Marketplace page, for visitors who do not have the extension yet. */ + /** Marketplace page, linked from the caption so the install is explained, not hidden. */ vscodeMarketplaceUrl: string; /** Full guide for each path, linked from the footer of the matching panel. */ dockerDocsUrl: string; + /** + * The guide's setup section, which lists every other way to open the wizard. This is the + * only place the panel points at when the deep link does nothing. + */ vscodeDocsUrl: string; }; @@ -49,6 +53,16 @@ function StepList({ steps }: { steps: QuickStartStep[] }) { ); } +/** + * Shown only after the setup button is used. VS Code's install-on-link can fail with + * "No extension gallery service configured" when the link is also what starts VS Code and a + * managed marketplace policy delays the gallery until the account is verified; a second + * click once VS Code is up succeeds. Rendering this after the click keeps that caveat out of + * the happy path for everyone who has not clicked yet. + */ +export const setupRetryHint = + "VS Code should now open and offer to install the extension. If VS Code had to start first, it can report an error before it is ready. Select Set up in VS Code again once it has loaded."; + export default function QuickStartTabs({ dockerCommand, dockerSteps, @@ -59,6 +73,7 @@ export default function QuickStartTabs({ vscodeDocsUrl, }: QuickStartTabsProps) { const [activeTab, setActiveTab] = useState("terminal"); + const [setupOpened, setSetupOpened] = useState(false); const tabRefs = useRef>({}); const selectTab = (id: TabId) => { @@ -168,37 +183,69 @@ export default function QuickStartTabs({ aria-labelledby="quickstart-tab-vscode" hidden={activeTab !== "vscode"} > -

    - This path needs Docker Desktop or Docker Engine, set to Linux - containers, running wherever VS Code is: your machine, or your WSL, - dev container, or SSH remote. The extension pulls the image, starts - it, and saves the connection for you. It never installs Docker, and it - changes nothing else on your machine. + {/* + No Docker prerequisite here. Both paths need Docker and the Terminal tab does not say + so, so saying it only here made the guided path look like the one with extra + requirements. The guide covers Docker properly, readiness states included. + */} +

    + + Choose this for the smoothest experience. + {" "} + VS Code sets up DocumentDB Local and creates a ready-to-use connection + for you. One click, then follow the wizard.

    -
    + {/* + One button carries the whole flow. VS Code itself offers to install a missing + extension when a vscode:// link targets it, then re-opens the link, so a separate + "Install the extension" action was a step the visitor never has to take. + */} + setSetupOpened(true)} + className="inline-flex w-full items-center justify-center rounded-md bg-blue-500 px-6 py-3 text-sm font-semibold text-white transition-colors hover:bg-blue-400 sm:w-auto" + > + Set up in VS Code + +

    + Opens VS Code, asks to install the{" "} - Install the extension - - {" "} + extension if you do not have it yet, and launches the wizard. +

    + {setupOpened && ( +

    - Open setup in VS Code - -

    + {setupRetryHint} +

    + )} + {/* + Troubleshooting stays out of the happy path: a link, after the steps, rather than a + "if nothing happens" paragraph in front of someone who has not clicked yet. + */}

    - If nothing happens, check that the extension is installed and up to - date, then run{" "} - - DocumentDB: Set up DocumentDB Local - {" "} - from the Command Palette. + Not working in VS Code? The{" "} + + setup guide + {" "} + shows how to open the wizard from the activity bar or the Command + Palette.

    Prefer to start it yourself? The{" "} @@ -212,14 +259,6 @@ export default function QuickStartTabs({ {" "} tab runs the same image with one command.

    -
    - - Full VS Code guide - -
    ); diff --git a/app/page.tsx b/app/page.tsx index c26b8d7..46be844 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,6 +6,7 @@ import { documentdbVsCodeExtensionMarketplaceUrl, documentdbVsCodeLocalQuickStartDeepLink, } from "./services/externalLinks"; +import { vscodeSetupSectionAnchor } from "./services/articleService"; import { getMetadata } from "./services/metadataService"; import { documentdbGitHubForks, @@ -61,21 +62,19 @@ const dockerQuickStartSteps = [ }, ]; +// Two steps against Terminal's three: the guided path should look shorter at a glance. +// Installing the extension is not a step, because VS Code offers to do it when the deep +// link is opened; the caption under the button says so. const vscodeQuickStartSteps = [ { step: "01", description: - "Install the free DocumentDB for VS Code extension from the Marketplace.", + "Once VS Code loads the wizard, select Continue, review the defaults, then select Start DocumentDB Local.", }, { step: "02", description: - "Select Open setup in VS Code, then confirm both prompts. In the wizard select Continue, review the defaults, and select Start DocumentDB Local.", - }, - { - step: "03", - description: - "You get a container on an available port, 10260 unless it is taken, with generated credentials. Select Open Connection to browse data and run your first query.", + "Select Open Connection to browse your data and run your first query. Sample data is included.", }, ]; @@ -411,7 +410,7 @@ export default function Home() { vscodeDeepLinkUrl={documentdbVsCodeLocalQuickStartDeepLink} vscodeMarketplaceUrl={documentdbVsCodeExtensionMarketplaceUrl} dockerDocsUrl="/docs/getting-started/docker" - vscodeDocsUrl="/docs/getting-started/vscode-quickstart" + vscodeDocsUrl={`/docs/getting-started/vscode-quickstart#${vscodeSetupSectionAnchor}`} /> diff --git a/app/services/articleService.ts b/app/services/articleService.ts index 5b41cc4..9fd588a 100644 --- a/app/services/articleService.ts +++ b/app/services/articleService.ts @@ -1,4 +1,5 @@ import fs from 'fs'; +import { kebabCase } from 'change-case'; import path from 'path'; import { load as loadYaml } from 'js-yaml'; import matter from 'gray-matter'; @@ -614,6 +615,14 @@ If the target already has PostgreSQL, the PGDG extension dependencies (\`postgre - **Full stack from the release assets** — pass the five packages for the selected PostgreSQL major to a *single* \`apt install\` / \`dnf install\`: \`documentdb-N\`, the matching \`postgresql-N-documentdb\` / \`postgresqlN-documentdb\` extension, \`documentdb-common\`, \`documentdb-gateway\`, and \`documentdb-postgresql-tools\`. For PostgreSQL 18 only, the optional \`documentdb\` meta package may be included; it selects \`documentdb-18\`. Local files resolve dependencies only against enabled repositories, so a package whose dependencies are not included still fails. `; +/** + * The guide section the homepage quick start links to when the deep link does nothing. The + * anchor is derived exactly as Markdown.tsx derives H2 anchors, so a retitled heading moves + * the link with it instead of silently breaking it. + */ +const vscodeSetupSectionTitle = 'Set up DocumentDB Local'; +export const vscodeSetupSectionAnchor = kebabCase(vscodeSetupSectionTitle); + const vscodeQuickStartGuideContent = `# Visual Studio Code Quick Start Use DocumentDB for VS Code to set up a local DocumentDB instance, browse sample data, and create your first database without leaving the editor. @@ -639,14 +648,14 @@ code --install-extension ms-azuretools.vscode-documentdb If VS Code prompts you to reload after installation, do that before creating a connection. -## Set up DocumentDB Local +## ${vscodeSetupSectionTitle} This is the fastest path, and it leaves no Docker commands for you to run. 1. Open setup using any of these: - Select the DocumentDB icon in the activity bar, expand **Your own DocumentDB** in the Connections view, and select **Set up DocumentDB Local**. - Run **DocumentDB: Set up DocumentDB Local** from the Command Palette. - - Paste \`vscode://ms-azuretools.vscode-documentdb/local\` into your browser address bar and confirm both prompts. This needs extension version 0.10.1 or later. + - Open \`vscode://ms-azuretools.vscode-documentdb/local\` from your browser and confirm the prompts. If the extension is not installed, VS Code offers to install it first. This needs extension version 0.10.1 or later. 2. On the **Introduction** step, select **Continue**. Nothing is downloaded or created until the next step. 3. On the **Configure** step, review the defaults and select **Start DocumentDB Local**. The defaults give you an available port (starting at \`10260\`), generated credentials, the \`latest\` official image, and optional sample data. Expand the advanced options to set the port, image tag, or credentials yourself. 4. Wait for setup to finish. The extension creates a container named \`vscode-documentdb-local\` with a persistent volume, then waits until the database accepts connections. @@ -713,6 +722,7 @@ After the connection works, the extension can help you continue without leaving If setup or the connection does not work on the first try: - If the browser link does nothing, confirm the extension is installed and up to date, then run **DocumentDB: Set up DocumentDB Local** from the Command Palette instead +- If VS Code reports **No extension gallery service configured**, it could not reach a marketplace to install the extension for you. On managed devices whose policy points VS Code at a private marketplace, the gallery is unavailable until VS Code has verified your account, so the first click can fail when it is also what starts VS Code. Select the button again once VS Code has loaded, or install the extension yourself with \`code --install-extension ms-azuretools.vscode-documentdb\` and then open the link again - If setup reports that Docker is unreachable, fix what it names (Docker not running, or Docker set to Windows containers rather than Linux) and select **Continue setup**; nothing has been created at that point - Verify the extension is installed and reload VS Code if the DocumentDB view does not appear - Confirm your local DocumentDB instance is actually running before you connect diff --git a/tests/quickStart.test.ts b/tests/quickStart.test.ts index 778421d..74ee97f 100644 --- a/tests/quickStart.test.ts +++ b/tests/quickStart.test.ts @@ -2,6 +2,11 @@ import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { describe, expect, it } from 'vitest'; import Home from '../app/page'; +import { setupRetryHint } from '../app/components/QuickStartTabs'; +import { + getArticleByPath, + vscodeSetupSectionAnchor, +} from '../app/services/articleService'; import { documentdbVsCodeExtensionMarketplaceUrl, documentdbVsCodeLocalQuickStartDeepLink, @@ -12,6 +17,18 @@ const html = renderToStaticMarkup(createElement(Home)); /** The quick start card only, so assertions cannot be satisfied by unrelated page content. */ const card = html.slice(html.indexOf('id="run-with-docker"')); +/** One tab panel only: bounded by the next panel, or by the end of the hero section. */ +function panel(id: 'terminal' | 'vscode') { + const start = card.indexOf(`id="quickstart-panel-${id}"`); + expect(start).toBeGreaterThan(-1); + const end = [card.indexOf('id="quickstart-panel-vscode"', start + 1), card.indexOf('
    ', start)] + .filter((index) => index > start) + .reduce((nearest, index) => Math.min(nearest, index)); + return card.slice(start, end); +} + +const vscodeGuideUrl = `/docs/getting-started/vscode-quickstart#${vscodeSetupSectionAnchor}`; + describe('homepage local quick start', () => { it('preserves the public anchor and the terminal path as the default tab', () => { expect(html).toContain('id="run-with-docker"'); @@ -55,36 +72,47 @@ describe('homepage local quick start', () => { expect(card).toContain('Both run the same DocumentDB Local image.'); }); - it('leads with installing the extension, then opening setup', () => { - expect(documentdbVsCodeExtensionMarketplaceUrl).toBe( - 'https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-documentdb', - ); + it('carries the whole VS Code flow on one button, with the install explained underneath', () => { expect(documentdbVsCodeLocalQuickStartDeepLink).toBe( 'vscode://ms-azuretools.vscode-documentdb/local', ); - const marketplace = card.indexOf( - `href="${documentdbVsCodeExtensionMarketplaceUrl}"`, + const vscode = panel('vscode'); + + // VS Code offers to install a missing extension when the deep link targets it, so a + // separate "Install the extension" action was a step the visitor never has to take. + expect(vscode).toMatch( + new RegExp(`]*>Set up in VS Code`), ); - const setup = card.indexOf( - `href="${documentdbVsCodeLocalQuickStartDeepLink}"`, + expect(vscode).not.toContain('Install the extension'); + expect(vscode).not.toContain('Open setup in VS Code'); + + // The caption is what the button is described by, and it is where the Marketplace link + // lives now: explained, not hidden, but not a second button. + expect(vscode).toMatch( + / { - expect(card).toContain('set to Linux'); - expect(card).toContain('running wherever VS Code is'); - // "changes your system" was false: it creates a container and a persistent volume. - expect(card).toContain('changes nothing else on your machine'); + it('does not put a Docker prerequisite only under the guided path', () => { + // Both paths need Docker and the Terminal tab does not say so; saying it only under VS + // Code made the guided path look like the one with extra requirements. + expect(panel('vscode')).not.toMatch(/Docker/); + expect(panel('vscode')).not.toContain('Linux containers'); }); - it('names both wizard clicks and does not guarantee a port it may not get', () => { - expect(card).toContain('select Continue'); - expect(card).toContain('select Start DocumentDB Local'); - expect(card).toContain('on an available port, 10260 unless it is taken'); + it('shows the guided path as two steps, the wizard and the payoff', () => { + expect(panel('terminal').match(/ { @@ -96,16 +124,25 @@ describe('homepage local quick start', () => { expect(card).not.toMatch(/\bv\d+\.\d+\.\d+\b/); }); - it('links both full guides from inside their own panels', () => { + it('links both guides from inside their own panels', () => { // Conditionally rendering one link left the VS Code guide out of the exported HTML // entirely, since the server renders with the terminal tab active. - expect(card).toContain('href="/docs/getting-started/docker"'); - expect(card).toContain('href="/docs/getting-started/vscode-quickstart"'); - expect(card).toContain('Full Docker guide'); - expect(card).toContain('Full VS Code guide'); + expect(panel('terminal')).toContain('href="/docs/getting-started/docker"'); + expect(panel('terminal')).toContain('Full Docker guide'); + expect(panel('vscode')).toContain(`href="${vscodeGuideUrl}"`); + }); - const vscodePanel = card.slice(card.indexOf('id="quickstart-panel-vscode"')); - expect(vscodePanel).toContain('href="/docs/getting-started/vscode-quickstart"'); + it('points the VS Code fallback at a section the guide actually has', () => { + // Markdown.tsx anchors each H2 with kebabCase(title); the link must land on the section + // that lists the other ways to open the wizard, not at the top of the page. + const guide = getArticleByPath('getting-started', ['vscode-quickstart']); + expect(guide?.content).toContain('## Set up DocumentDB Local'); + expect(vscodeSetupSectionAnchor).toBe('set-up-document-db-local'); + expect(guide?.content).toContain('VS Code offers to install it first'); + // The install-on-link flow fails on machines whose policy points VS Code at a private + // marketplace before the account check completes; the guide names that error verbatim. + expect(guide?.content).toContain('No extension gallery service configured'); + expect(guide?.content).toContain('code --install-extension ms-azuretools.vscode-documentdb'); }); it('offers a labelled route between the two paths', () => { @@ -113,11 +150,21 @@ describe('homepage local quick start', () => { expect(card).toContain('aria-label="Switch to the Terminal tab"'); }); - it('provides a Command Palette fallback that names the likely cause', () => { - expect(card).toContain( - 'If nothing happens, check that the extension is installed and up to', - ); - expect(card).toContain('DocumentDB: Set up DocumentDB Local'); - expect(card).toContain('from the Command Palette.'); + it('keeps troubleshooting out of the happy path', () => { + const vscode = panel('vscode'); + expect(vscode).not.toContain('If nothing happens'); + // The retry hint exists only after the button is used; it must not be in the first paint. + expect(setupRetryHint).toContain('Select Set up in VS Code again'); + expect(vscode).not.toContain('again once it has loaded'); + expect(vscode).not.toContain('role="status"'); + expect(vscode).not.toContain('DocumentDB: Set up DocumentDB Local'); + + // A short link after the steps, not a paragraph of doubt in front of someone who has + // not clicked yet. + const fallback = vscode.indexOf('Not working in VS Code?'); + expect(fallback).toBeGreaterThan(vscode.lastIndexOf('')); + expect(vscode.slice(fallback)).toContain(`href="${vscodeGuideUrl}"`); + expect(vscode.slice(fallback)).toContain('>setup guide'); + expect(vscode.slice(fallback)).toContain('activity bar or the Command'); }); }); From 2c1eac7a067076636e58996123508e4fcb31ca10 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Fri, 11 Sep 2026 09:43:06 -0400 Subject: [PATCH 8/9] Show the retry line late, and tighten the VS Code caption Design review of the previous commit found the post-click hint fired instantly for everyone, so the majority for whom VS Code was already opening read "it can report an error" - the doubt in the happy path the reviewer asked to remove, moved one click later. It also promised "VS Code should now open" to people with no VS Code at all, for whom the link is silent. The hint is now one line that appears four seconds after the button is used: "Nothing happened? Try Set up in VS Code again, or install the extension from the Marketplace first." The deep link is repeated as the retry control, since on managed devices the second click is the one that works. The error itself is explained only in the guide. The live region is mounted from the first paint so the line is announced when it arrives. The caption is two short sentences with the same facts, and the footer link reads "full VS Code guide" to rhyme with the Terminal panel's "Full Docker guide", so a successful visitor still has a neutral route to the guide. --- app/components/QuickStartTabs.tsx | 68 ++++++++++++++++++++++--------- tests/quickStart.test.ts | 19 +++++---- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/app/components/QuickStartTabs.tsx b/app/components/QuickStartTabs.tsx index c957ca3..d0a370d 100644 --- a/app/components/QuickStartTabs.tsx +++ b/app/components/QuickStartTabs.tsx @@ -1,7 +1,7 @@ "use client"; import Link from "next/link"; -import { useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import CommandSnippet from "./CommandSnippet"; export type QuickStartStep = { @@ -54,14 +54,14 @@ function StepList({ steps }: { steps: QuickStartStep[] }) { } /** - * Shown only after the setup button is used. VS Code's install-on-link can fail with - * "No extension gallery service configured" when the link is also what starts VS Code and a - * managed marketplace policy delays the gallery until the account is verified; a second - * click once VS Code is up succeeds. Rendering this after the click keeps that caveat out of - * the happy path for everyone who has not clicked yet. + * How long after the setup button is used before the retry line appears. Long enough for + * VS Code to open and take focus on a normal machine, so people for whom it worked never + * read it; short enough that someone still looking at the page finds it. */ -export const setupRetryHint = - "VS Code should now open and offer to install the extension. If VS Code had to start first, it can report an error before it is ready. Select Set up in VS Code again once it has loaded."; +const RETRY_HINT_DELAY_MS = 4000; + +/** Lead of the retry line; exported so the test can assert it is absent from the first paint. */ +export const setupRetryLead = "Nothing happened?"; export default function QuickStartTabs({ dockerCommand, @@ -74,6 +74,20 @@ export default function QuickStartTabs({ }: QuickStartTabsProps) { const [activeTab, setActiveTab] = useState("terminal"); const [setupOpened, setSetupOpened] = useState(false); + const [showRetry, setShowRetry] = useState(false); + + // The deep link can do nothing visible: no VS Code installed, or on managed devices whose + // policy sets a private marketplace, a cold-started VS Code fails the install with "No + // extension gallery service configured" and a second click succeeds. A retry line shown + // only after a delay reaches those people without putting doubt in front of the majority + // for whom VS Code is already opening. + useEffect(() => { + if (!setupOpened) { + return; + } + const timer = window.setTimeout(() => setShowRetry(true), RETRY_HINT_DELAY_MS); + return () => window.clearTimeout(timer); + }, [setupOpened]); const tabRefs = useRef>({}); const selectTab = (id: TabId) => { @@ -212,7 +226,7 @@ export default function QuickStartTabs({ id="quickstart-vscode-setup-caption" className="mt-2.5 text-sm leading-6 text-gray-400" > - Opens VS Code, asks to install the{" "} + Opens VS Code and its setup wizard. Installs the{" "} DocumentDB for VS Code {" "} - extension if you do not have it yet, and launches the wizard. + extension first if you need it.

    - {setupOpened && ( -

    - {setupRetryHint} -

    - )} + {/* Always mounted so the live region exists before its content arrives. */} +
    + {showRetry && ( +

    + {setupRetryLead} Try{" "} + + Set up in VS Code + {" "} + again, or install the extension from the{" "} + + Marketplace + {" "} + first. +

    + )} +
    {/* Troubleshooting stays out of the happy path: a link, after the steps, rather than a @@ -242,7 +272,7 @@ export default function QuickStartTabs({ href={vscodeDocsUrl} className="font-semibold text-blue-300 transition-colors hover:text-blue-200" > - setup guide + full VS Code guide {" "} shows how to open the wizard from the activity bar or the Command Palette. diff --git a/tests/quickStart.test.ts b/tests/quickStart.test.ts index 74ee97f..1c86a9a 100644 --- a/tests/quickStart.test.ts +++ b/tests/quickStart.test.ts @@ -2,7 +2,7 @@ import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { describe, expect, it } from 'vitest'; import Home from '../app/page'; -import { setupRetryHint } from '../app/components/QuickStartTabs'; +import { setupRetryLead } from '../app/components/QuickStartTabs'; import { getArticleByPath, vscodeSetupSectionAnchor, @@ -92,9 +92,9 @@ describe('homepage local quick start', () => { / { it('keeps troubleshooting out of the happy path', () => { const vscode = panel('vscode'); expect(vscode).not.toContain('If nothing happens'); - // The retry hint exists only after the button is used; it must not be in the first paint. - expect(setupRetryHint).toContain('Select Set up in VS Code again'); - expect(vscode).not.toContain('again once it has loaded'); - expect(vscode).not.toContain('role="status"'); + // The retry line appears only some seconds after the button is used. The live region is + // mounted from the first paint so it is announced when filled, but it starts empty. + expect(setupRetryLead).toBe('Nothing happened?'); + expect(vscode).not.toContain(setupRetryLead); + expect(vscode).toContain('
    '); expect(vscode).not.toContain('DocumentDB: Set up DocumentDB Local'); // A short link after the steps, not a paragraph of doubt in front of someone who has @@ -164,7 +165,9 @@ describe('homepage local quick start', () => { const fallback = vscode.indexOf('Not working in VS Code?'); expect(fallback).toBeGreaterThan(vscode.lastIndexOf('')); expect(vscode.slice(fallback)).toContain(`href="${vscodeGuideUrl}"`); - expect(vscode.slice(fallback)).toContain('>setup guide
    '); + // Same link text shape as the Terminal panel's "Full Docker guide", so the two panels + // rhyme and a successful visitor still has a neutral route to the guide. + expect(vscode.slice(fallback)).toContain('>full VS Code guide'); expect(vscode.slice(fallback)).toContain('activity bar or the Command'); }); }); From 8e51bad68e6937941de648f14dad7f5ce38b82f3 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Fri, 11 Sep 2026 09:47:51 -0400 Subject: [PATCH 9/9] Derive guide anchors in one place, and move the retry line below the steps Code review of the previous two commits, applied: - The homepage imported the docs content service to obtain one anchor string, and Markdown.tsx and articleService.ts each called kebabCase independently, so the "renderer and link cannot drift" claim was only true by coincidence. app/lib/docsAnchors.ts now owns headingAnchor and the setup-section constants; the renderer, the guide and the homepage all use it, and the homepage no longer imports a module that touches fs. - The retry line arrived above the step list, shifting the steps down while they were being read. It now renders below the steps, next to the footer it belongs with, as an exported SetupRetryHint component so the visible state is covered by a test without a DOM library. - Copy: VS Code offers to install the extension, it does not install it unasked; the flow has prompts to confirm; sample data is loaded by default rather than unconditionally included. The guide's gallery bullet keeps the symptom and both fixes and drops the unverified mechanism, and says "open the link again" since the guide has no button. - Tests no longer pin attribute order or exact empty-element markup, bound the caption to its paragraph, and fail with an assertion rather than a TypeError if a panel boundary moves. --- app/components/Markdown.tsx | 4 +- app/components/QuickStartTabs.tsx | 106 +++++++++++++++++------------- app/lib/docsAnchors.ts | 13 ++++ app/page.tsx | 4 +- app/services/articleService.ts | 12 +--- tests/quickStart.test.ts | 67 ++++++++++++------- 6 files changed, 126 insertions(+), 80 deletions(-) create mode 100644 app/lib/docsAnchors.ts diff --git a/app/components/Markdown.tsx b/app/components/Markdown.tsx index 3849e3a..a4f48a3 100644 --- a/app/components/Markdown.tsx +++ b/app/components/Markdown.tsx @@ -6,7 +6,7 @@ import Link from "next/link"; import { useMemo } from 'react'; import type { ReactElement } from 'react'; import Code from './Code'; -import { kebabCase } from 'change-case'; +import { headingAnchor } from '../lib/docsAnchors'; import { resolveMarkdownLink } from '../lib/markdownLinks'; interface MarkdownProps { @@ -43,7 +43,7 @@ export default function Markdown({ content, sourcePath }: MarkdownProps) { elements.push(
    - +

    {title}

    diff --git a/app/components/QuickStartTabs.tsx b/app/components/QuickStartTabs.tsx index d0a370d..3d3eb6b 100644 --- a/app/components/QuickStartTabs.tsx +++ b/app/components/QuickStartTabs.tsx @@ -17,11 +17,11 @@ type QuickStartTabsProps = { vscodeDeepLinkUrl: string; /** Marketplace page, linked from the caption so the install is explained, not hidden. */ vscodeMarketplaceUrl: string; - /** Full guide for each path, linked from the footer of the matching panel. */ + /** Full Docker guide, linked from the Terminal panel's footer. */ dockerDocsUrl: string; /** - * The guide's setup section, which lists every other way to open the wizard. This is the - * only place the panel points at when the deep link does nothing. + * The VS Code guide's setup section, which lists every other way to open the wizard. + * Linked from the VS Code panel's footer. */ vscodeDocsUrl: string; }; @@ -55,13 +55,53 @@ function StepList({ steps }: { steps: QuickStartStep[] }) { /** * How long after the setup button is used before the retry line appears. Long enough for - * VS Code to open and take focus on a normal machine, so people for whom it worked never - * read it; short enough that someone still looking at the page finds it. + * VS Code to open and take focus on a normal machine; short enough that someone still + * looking at the page finds it. It is phrased so it does no harm if setup did work. */ const RETRY_HINT_DELAY_MS = 4000; -/** Lead of the retry line; exported so the test can assert it is absent from the first paint. */ -export const setupRetryLead = "Nothing happened?"; +/** + * The retry line under the VS Code steps. The deep link can do nothing visible: no VS Code + * installed, or on managed devices whose policy sets a private marketplace, a cold-started + * VS Code fails the install and a second click succeeds. The live region is always mounted + * so it exists before its content arrives, which is what makes the arrival announced. + * Exported so the visible state can be rendered in a test without a DOM library. + */ +export function SetupRetryHint({ + visible, + deepLinkUrl, + marketplaceUrl, +}: { + visible: boolean; + deepLinkUrl: string; + marketplaceUrl: string; +}) { + return ( +
    + {visible && ( +

    + Nothing happened? Try{" "} + + Set up in VS Code + {" "} + again, or install the extension from the{" "} + + Marketplace + {" "} + first. +

    + )} +
    + ); +} export default function QuickStartTabs({ dockerCommand, @@ -73,14 +113,11 @@ export default function QuickStartTabs({ vscodeDocsUrl, }: QuickStartTabsProps) { const [activeTab, setActiveTab] = useState("terminal"); + // Deliberately never reset on a tab switch: the advice stays true once the link was used. const [setupOpened, setSetupOpened] = useState(false); const [showRetry, setShowRetry] = useState(false); + const tabRefs = useRef>({}); - // The deep link can do nothing visible: no VS Code installed, or on managed devices whose - // policy sets a private marketplace, a cold-started VS Code fails the install with "No - // extension gallery service configured" and a second click succeeds. A retry line shown - // only after a delay reaches those people without putting doubt in front of the majority - // for whom VS Code is already opening. useEffect(() => { if (!setupOpened) { return; @@ -88,7 +125,6 @@ export default function QuickStartTabs({ const timer = window.setTimeout(() => setShowRetry(true), RETRY_HINT_DELAY_MS); return () => window.clearTimeout(timer); }, [setupOpened]); - const tabRefs = useRef>({}); const selectTab = (id: TabId) => { setActiveTab(id); @@ -207,7 +243,7 @@ export default function QuickStartTabs({ Choose this for the smoothest experience. {" "} VS Code sets up DocumentDB Local and creates a ready-to-use connection - for you. One click, then follow the wizard. + for you. One click, confirm the prompts, then follow the wizard.

    {/* One button carries the whole flow. VS Code itself offers to install a missing @@ -224,48 +260,30 @@ export default function QuickStartTabs({

    - Opens VS Code and its setup wizard. Installs the{" "} + Opens VS Code and its setup wizard. If you do not have the{" "} DocumentDB for VS Code {" "} - extension first if you need it. + extension, VS Code offers to install it first.

    - {/* Always mounted so the live region exists before its content arrives. */} -
    - {showRetry && ( -

    - {setupRetryLead} Try{" "} - - Set up in VS Code - {" "} - again, or install the extension from the{" "} - - Marketplace - {" "} - first. -

    - )} -
    {/* - Troubleshooting stays out of the happy path: a link, after the steps, rather than a - "if nothing happens" paragraph in front of someone who has not clicked yet. + Troubleshooting stays out of the happy path: below the steps, so its arrival never + shifts what is being read, and after a delay, so people for whom VS Code is already + opening are not shown doubt. */} +

    Not working in VS Code? The{" "} ', start)] - .filter((index) => index > start) - .reduce((nearest, index) => Math.min(nearest, index)); - return card.slice(start, end); + const candidates = [ + card.indexOf('id="quickstart-panel-vscode"', start + 1), + card.indexOf('', start), + ].filter((index) => index > start); + expect(candidates.length).toBeGreaterThan(0); + return card.slice(start, Math.min(...candidates)); } const vscodeGuideUrl = `/docs/getting-started/vscode-quickstart#${vscodeSetupSectionAnchor}`; @@ -80,21 +84,21 @@ describe('homepage local quick start', () => { // VS Code offers to install a missing extension when the deep link targets it, so a // separate "Install the extension" action was a step the visitor never has to take. - expect(vscode).toMatch( - new RegExp(`]*>Set up in VS Code`), - ); + // Attribute by attribute rather than as one sequence, so a JSX prop reorder cannot fail it. + const button = vscode.slice(0, vscode.indexOf('>Set up in VS Code')); + expect(button).toContain(`href="${documentdbVsCodeLocalQuickStartDeepLink}"`); + expect(button).toContain('aria-describedby="quickstart-vscode-setup-caption"'); expect(vscode).not.toContain('Install the extension'); expect(vscode).not.toContain('Open setup in VS Code'); // The caption is what the button is described by, and it is where the Marketplace link // lives now: explained, not hidden, but not a second button. - expect(vscode).toMatch( - /', captionStart)); expect(caption).toContain('Opens VS Code and its setup wizard.'); expect(caption).toContain(`href="${documentdbVsCodeExtensionMarketplaceUrl}"`); - expect(caption).toContain('extension first if you need it.'); + // VS Code offers; the visitor confirms. "Installs" promised more than the flow does. + expect(caption).toContain('VS Code offers to install it first.'); expect(vscode).toContain('Choose this for the smoothest experience.'); }); @@ -102,7 +106,7 @@ describe('homepage local quick start', () => { it('does not put a Docker prerequisite only under the guided path', () => { // Both paths need Docker and the Terminal tab does not say so; saying it only under VS // Code made the guided path look like the one with extra requirements. - expect(panel('vscode')).not.toMatch(/Docker/); + expect(panel('vscode')).not.toMatch(/docker/i); expect(panel('vscode')).not.toContain('Linux containers'); }); @@ -112,7 +116,7 @@ describe('homepage local quick start', () => { expect(panel('vscode')).toContain('select Continue'); expect(panel('vscode')).toContain('select Start DocumentDB Local'); // loadSampleData defaults to true in the extension's quickStartTypes.ts. - expect(panel('vscode')).toContain('Sample data is included.'); + expect(panel('vscode')).toContain('Sample data is loaded by default.'); }); it('carries no pinned extension version, which the guide owns instead', () => { @@ -136,7 +140,8 @@ describe('homepage local quick start', () => { // Markdown.tsx anchors each H2 with kebabCase(title); the link must land on the section // that lists the other ways to open the wizard, not at the top of the page. const guide = getArticleByPath('getting-started', ['vscode-quickstart']); - expect(guide?.content).toContain('## Set up DocumentDB Local'); + expect(guide?.content).toContain(`## ${vscodeSetupSectionTitle}`); + expect(vscodeSetupSectionAnchor).toBe(headingAnchor('Set up DocumentDB Local')); expect(vscodeSetupSectionAnchor).toBe('set-up-document-db-local'); expect(guide?.content).toContain('VS Code offers to install it first'); // The install-on-link flow fails on machines whose policy points VS Code at a private @@ -154,10 +159,11 @@ describe('homepage local quick start', () => { const vscode = panel('vscode'); expect(vscode).not.toContain('If nothing happens'); // The retry line appears only some seconds after the button is used. The live region is - // mounted from the first paint so it is announced when filled, but it starts empty. - expect(setupRetryLead).toBe('Nothing happened?'); - expect(vscode).not.toContain(setupRetryLead); - expect(vscode).toContain('

    '); + // mounted from the first paint so it is announced when filled, but it starts empty, and + // it sits below the steps so its arrival never shifts what is being read. + expect(vscode).not.toContain('Nothing happened?'); + expect(vscode).toMatch(/
    \s*<\/div>/); + expect(vscode.search(/
    /)).toBeGreaterThan(vscode.lastIndexOf('')); expect(vscode).not.toContain('DocumentDB: Set up DocumentDB Local'); // A short link after the steps, not a paragraph of doubt in front of someone who has @@ -170,4 +176,21 @@ describe('homepage local quick start', () => { expect(vscode.slice(fallback)).toContain('>full VS Code guide'); expect(vscode.slice(fallback)).toContain('activity bar or the Command'); }); + + it('repeats the deep link as the retry control once the hint is visible', () => { + const hint = renderToStaticMarkup( + createElement(SetupRetryHint, { + visible: true, + deepLinkUrl: documentdbVsCodeLocalQuickStartDeepLink, + marketplaceUrl: documentdbVsCodeExtensionMarketplaceUrl, + }), + ); + expect(hint).toContain('Nothing happened?'); + // The second click is the one that works on managed devices, so it is one action away. + expect(hint).toContain(`href="${documentdbVsCodeLocalQuickStartDeepLink}"`); + expect(hint).toContain('>Set up in VS Code'); + expect(hint).toContain(`href="${documentdbVsCodeExtensionMarketplaceUrl}"`); + // Nothing about the error itself: that explanation belongs in the guide. + expect(hint).not.toMatch(/error/i); + }); });