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
new file mode 100644
index 0000000..3d3eb6b
--- /dev/null
+++ b/app/components/QuickStartTabs.tsx
@@ -0,0 +1,313 @@
+"use client";
+
+import Link from "next/link";
+import { useEffect, 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, linked from the caption so the install is explained, not hidden. */
+ vscodeMarketplaceUrl: string;
+ /** Full Docker guide, linked from the Terminal panel's footer. */
+ dockerDocsUrl: string;
+ /**
+ * 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;
+};
+
+// "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" },
+] as const;
+
+type TabId = (typeof TABS)[number]["id"];
+
+function StepList({ steps }: { steps: QuickStartStep[] }) {
+ return (
+
+ {steps.map((item) => (
+
+
+ {item.step}
+
+
{item.description}
+
+ ))}
+
+ );
+}
+
+/**
+ * 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; 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;
+
+/**
+ * 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,
+ dockerSteps,
+ vscodeSteps,
+ vscodeDeepLinkUrl,
+ vscodeMarketplaceUrl,
+ dockerDocsUrl,
+ 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>({});
+
+ useEffect(() => {
+ if (!setupOpened) {
+ return;
+ }
+ const timer = window.setTimeout(() => setShowRetry(true), RETRY_HINT_DELAY_MS);
+ return () => window.clearTimeout(timer);
+ }, [setupOpened]);
+
+ 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. Home/End jump to the ends, per the
+ // ARIA authoring practices for tabs.
+ const onTabKeyDown = (event: React.KeyboardEvent) => {
+ const currentIndex = TABS.findIndex((tab) => tab.id === activeTab);
+
+ 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 (
+
+ Want a GUI? The{" "}
+ {" "}
+ connects to this container too.
+
+
+
+ Full Docker guide
+
+
+
+
+
+ {/*
+ 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, confirm the prompts, then follow the wizard.
+
+ Opens VS Code and its setup wizard. If you do not have the{" "}
+
+ DocumentDB for VS Code
+ {" "}
+ extension, VS Code offers to install it first.
+
+
+ {/*
+ 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{" "}
+
+ full VS Code guide
+ {" "}
+ shows how to open the wizard from the activity bar or the Command
+ Palette.
+
+
+ Prefer to start it yourself? The{" "}
+ {" "}
+ tab runs the same image with one command.
+
+
+
+ );
+}
diff --git a/app/lib/docsAnchors.ts b/app/lib/docsAnchors.ts
new file mode 100644
index 0000000..370341f
--- /dev/null
+++ b/app/lib/docsAnchors.ts
@@ -0,0 +1,13 @@
+import { kebabCase } from 'change-case';
+
+/**
+ * Anchor id for a guide H2, exactly as Markdown.tsx emits it. Anything that links into a
+ * guide section derives the fragment here, so the renderer and the link cannot disagree.
+ */
+export function headingAnchor(title: string): string {
+ return kebabCase(title);
+}
+
+/** The guide section the homepage quick start points at when the VS Code deep link does nothing. */
+export const vscodeSetupSectionTitle = 'Set up DocumentDB Local';
+export const vscodeSetupSectionAnchor = headingAnchor(vscodeSetupSectionTitle);
diff --git a/app/page.tsx b/app/page.tsx
index a8a7098..49dc8ab 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,7 +1,12 @@
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 { vscodeSetupSectionAnchor } from "./lib/docsAnchors";
import { getMetadata } from "./services/metadataService";
import {
documentdbGitHubForks,
@@ -34,23 +39,42 @@ 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 quickStartSteps = [
+const dockerQuickStartSteps = [
{
step: "01",
- description: "Run DocumentDB Local with Docker.",
+ description:
+ "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 your app, shell, or client.",
+ description:
+ "Connect on 127.0.0.1:10260 with mongosh (TLS flags are in the guide), a 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 a flag for sample data.",
+ },
+];
+
+// 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:
+ "Once VS Code loads the wizard, select Continue, review the defaults, then select Start DocumentDB Local.",
+ },
+ {
+ step: "02",
+ description:
+ "Select Open Connection to browse your data and run your first query. Sample data is loaded by default.",
},
];
@@ -316,7 +340,7 @@ export default function Home() {
-
+
Open source document database
@@ -372,43 +396,22 @@ export default function Home() {
Quick start
- Run locally with Docker
+ Run DocumentDB locally
- Start DocumentDB Local with Docker, then connect on port
- 10260.
+ Run it from your terminal, or let the VS Code extension set it
+ up for you. Both run the same DocumentDB Local image.
diff --git a/app/services/articleService.ts b/app/services/articleService.ts
index 2be8647..0bcac25 100644
--- a/app/services/articleService.ts
+++ b/app/services/articleService.ts
@@ -1,5 +1,6 @@
import fs from 'fs';
import path from 'path';
+import { vscodeSetupSectionTitle } from '../lib/docsAnchors';
import { load as loadYaml } from 'js-yaml';
import matter from 'gray-matter';
import { Article } from '../types/Article';
@@ -616,15 +617,19 @@ If the target already has PostgreSQL, the PGDG extension dependencies (\`postgre
const vscodeQuickStartGuideContent = `# Visual Studio Code Quick Start
-Use DocumentDB for VS Code to connect to a local DocumentDB instance, browse sample data, and create your first database without leaving the editor.
+Use DocumentDB for VS Code to set up a local DocumentDB instance, browse sample data, and create your first database without leaving the editor.
+
+The extension can create the instance for you: it pulls the official image, starts the container, waits until the database accepts connections, and saves the connection. It never installs Docker, and it changes nothing else on your machine.
## Prerequisites
- [Visual Studio Code](https://code.visualstudio.com/)
- The [DocumentDB for VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-documentdb)
-- A local DocumentDB instance from [Docker Quick Start](/docs/getting-started/docker) or a host setup with a running DocumentDB gateway
+- Docker Desktop or Docker Engine, set to Linux containers, running wherever VS Code is
- Optional: [mongosh](https://www.mongodb.com/docs/mongodb-shell/install/) for independent connection checks
+Docker must be reachable from the environment VS Code runs in. If you work in WSL, a dev container, an SSH remote, or Codespaces, Docker needs to be available there rather than only on your host machine. Setup runs a readiness check and explains what to fix if it cannot reach Docker.
+
## Install the extension
Install the extension from the VS Code marketplace, or run:
@@ -635,9 +640,26 @@ code --install-extension ms-azuretools.vscode-documentdb
If VS Code prompts you to reload after installation, do that before creating a connection.
-## Start DocumentDB first
+## ${vscodeSetupSectionTitle}
-For the fastest local setup, start DocumentDB Local with Docker:
+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.
+ - 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.
+5. Select **Open Connection** to reveal the saved connection, then expand it to browse databases and collections.
+
+If you keep the sample data option, a \`sampledb\` database is created with \`users\`, \`products\`, \`orders\`, and \`analytics\` collections.
+
+Right-click the DocumentDB Local entry to **Start**, **Stop**, **Restart**, or **Delete Container**, and to **Copy Connection String**, **Copy Password**, or **View Logs**. Stopping and starting preserves your data; deleting removes the volume and the generated credentials permanently.
+
+## Alternative: start the container yourself
+
+Use this if you already run DocumentDB Local outside VS Code, or you want to manage the container yourself. Start it with Docker:
\`\`\`bash
docker run -dt --name documentdb \\
@@ -649,7 +671,7 @@ docker run -dt --name documentdb \\
If you prefer a host installation instead of Docker, use the [Linux Packages Quick Start](/docs/getting-started/packages) on a distribution in the current release matrix.
-## Add a local connection in VS Code
+Then add the connection by hand:
1. Open the **DocumentDB** view in the VS Code activity bar.
2. In the local connection area, select **DocumentDB Local** and start the **New Local Connection** flow.
@@ -689,8 +711,11 @@ After the connection works, the extension can help you continue without leaving
## Troubleshooting and debugging
-If the extension does not connect on the first try:
+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 that use a private marketplace, this can happen when the link is also what starts VS Code. Open the link 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
- If you used Docker, check \`docker ps\` and \`docker logs documentdb\`
diff --git a/app/services/externalLinks.ts b/app/services/externalLinks.ts
index 9445af0..7a3ada9 100644
--- a/app/services/externalLinks.ts
+++ b/app/services/externalLinks.ts
@@ -13,3 +13,9 @@ export const documentdbKubernetesOperatorQuickStartUrl =
export const documentdbKubernetesOperatorGitHubUrl =
'https://github.com/documentdb/documentdb-kubernetes-operator';
+
+export const documentdbVsCodeExtensionMarketplaceUrl =
+ 'https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-documentdb';
+
+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..08b517d
--- /dev/null
+++ b/tests/quickStart.test.ts
@@ -0,0 +1,196 @@
+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 {
+ headingAnchor,
+ vscodeSetupSectionAnchor,
+ vscodeSetupSectionTitle,
+} from '../app/lib/docsAnchors';
+import { getArticleByPath } from '../app/services/articleService';
+import {
+ documentdbVsCodeExtensionMarketplaceUrl,
+ documentdbVsCodeLocalQuickStartDeepLink,
+} from '../app/services/externalLinks';
+
+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 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}`;
+
+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"');
+
+ // 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 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('carries the whole VS Code flow on one button, with the install explained underneath', () => {
+ expect(documentdbVsCodeLocalQuickStartDeepLink).toBe(
+ 'vscode://ms-azuretools.vscode-documentdb/local',
+ );
+ 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.
+ // 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.
+ const captionStart = vscode.indexOf('id="quickstart-vscode-setup-caption"');
+ const caption = vscode.slice(captionStart, vscode.indexOf('', captionStart));
+ expect(caption).toContain('Opens VS Code and its setup wizard.');
+ expect(caption).toContain(`href="${documentdbVsCodeExtensionMarketplaceUrl}"`);
+ // 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.');
+ });
+
+ 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/i);
+ expect(panel('vscode')).not.toContain('Linux containers');
+ });
+
+ it('shows the guided path as two steps, the wizard and the payoff', () => {
+ expect(panel('terminal').match(/
{
+ // 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 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(panel('terminal')).toContain('href="/docs/getting-started/docker"');
+ expect(panel('terminal')).toContain('Full Docker guide');
+ expect(panel('vscode')).toContain(`href="${vscodeGuideUrl}"`);
+ });
+
+ 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(`## ${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
+ // 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', () => {
+ expect(card).toContain('aria-label="Switch to the VS Code tab"');
+ expect(card).toContain('aria-label="Switch to the Terminal tab"');
+ });
+
+ it('keeps troubleshooting out of the happy path', () => {
+ 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, 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
+ // not clicked yet.
+ const fallback = vscode.indexOf('Not working in VS Code?');
+ expect(fallback).toBeGreaterThan(vscode.lastIndexOf(''));
+ expect(vscode.slice(fallback)).toContain(`href="${vscodeGuideUrl}"`);
+ // 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');
+ });
+
+ 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);
+ });
+});
diff --git a/vitest.config.mts b/vitest.config.mts
new file mode 100644
index 0000000..21215b0
--- /dev/null
+++ b/vitest.config.mts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ oxc: {
+ jsx: {
+ runtime: 'automatic',
+ },
+ },
+});