microsoft/vscode/component-fixtures

Trust tier 4
  • community
  • verified
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when creating or updating component fixtures for screenshot testing or their shared infrastructure, or when designing UI components to be fixture-friendly. Covers fixture file structure, theming, service setup, CSS scoping, async rendering, validation, and common pitfalls.

Install

npx vskill@latest install microsoft/vscode/component-fixtures

SKILL.md

Component Fixtures

Component fixtures render isolated UI components for visual screenshot testing via the component explorer. Fixtures live in src/vs/workbench/test/browser/componentFixtures/ and are auto-discovered by the Vite dev server using the glob src/**/*.fixture.ts.

Use tools mcp_component-exp_* to list and screenshot fixtures. If you cannot see these tools, inform the user to them on.

Running Fixtures Locally

  1. Start the component explorer server: run the Component Explorer Server task
  2. Use the mcp_component-exp_list_fixtures tool to see all available fixtures and their URLs
  3. Use the mcp_component-exp_screenshot tool to capture screenshots programmatically

File Structure

Each fixture file exports a default defineThemedFixtureGroup(...). The file must end with .fixture.ts.

src/vs/workbench/test/browser/componentFixtures/
fixtureUtils.ts # Shared helpers (DO NOT import @vscode/component-explorer elsewhere)
myComponent.fixture.ts # Your fixture file

Basic Pattern

typescript
import { ComponentFixtureContext, createEditorServices, defineComponentFixture, defineThemedFixtureGroup } from './fixtureUtils.js';
export default defineThemedFixtureGroup({ path: 'myFeature/' }, {
Default: defineComponentFixture({ render: renderMyComponent }),
AnotherVariant: defineComponentFixture({ render: renderMyComponent }),
});
function renderMyComponent({ container, disposableStore, theme }: ComponentFixtureContext): void {
container.style.width = '400px';
const instantiationService = createEditorServices(disposableStore, {
colorTheme: theme,
additionalServices: (reg) => {
// Register additional services the component needs
reg.define(IMyService, MyServiceImpl);
reg.defineInstance(IMockService, mockInstance);
},
});
const widget = disposableStore.add(
instantiationService.createInstance(MyWidget, /* constructor args */)
);
container.appendChild(widget.domNode);
}

Key points:

  • `defineThemedFixtureGroup` automatically creates Dark and Light variants for each fixture
  • `defineComponentFixture` wraps your render function with theme setup, async readiness, and disposable management
  • `createEditorServices` provides a TestInstantiationService with base editor services pre-registered
  • Always register created widgets with disposableStore.add(...) to prevent leaks
  • Pass colorTheme: theme to createEditorServices so theme colors render correctly
File icon themes

Fixtures use Seti file icons by default. Select another built-in theme, or disable file icons, on the individual fixture:

typescript
defineComponentFixture({ fileIconTheme: 'vs-minimal', render: renderMyComponent });
defineComponentFixture({ fileIconTheme: 'none', render: renderMyComponent });

When the rendered component reads IThemeService, pass the selected theme from ComponentFixtureContext to createEditorServices:

typescript
function renderMyComponent({ disposableStore, theme, fileIconTheme }: ComponentFixtureContext): void {
const instantiationService = createEditorServices(disposableStore, {
colorTheme: theme,
fileIconTheme,
});
}

Utilities from fixtureUtils.ts

ExportPurpose
defineComponentFixtureCreates Dark/Light themed fixture variants from a render function
defineThemedFixtureGroupGroups multiple themed fixtures into a named fixture group
createEditorServicesCreates TestInstantiationService with all base editor services
registerWorkbenchServicesRegisters additional workbench services (context menu, label, etc.)
createTextModelCreates a text model via ModelService for editor fixtures
setupThemeApplies theme CSS to a container (called automatically by defineComponentFixture)
darkTheme / lightThemePre-loaded ColorThemeData instances

Important: Only fixtureUtils.ts may import from @vscode/component-explorer. All fixture files must go through the helpers in fixtureUtils.ts.

CSS Scoping

Fixtures share the document and workbench stylesheets (isolation: 'none'). The helpers scope theme and file-icon styles, but DOM, CSS, and process-wide registrations are not isolated automatically.

Shared theme initialization

fixtureUtilsCss.ts loads the shared workbench color and size registrations before generating and caching theme CSS, including the agent tokens in vs/workbench/common/agentsColors.ts and agentsSizes.ts. Keep required product-wide registrations in this shared bootstrap, not in individual fixtures: discovering another fixture must not change an existing fixture's typography or colors. Reuse lightweight registration modules from the owning shared layer rather than importing product entry points or higher-layer contributions.

Matching production CSS selectors

Many VS Code components have CSS rules scoped to deep ancestor selectors (e.g., .interactive-session .interactive-input-part > .widget-container .my-element). In fixtures, you must recreate the required ancestor DOM structure for these selectors to match:

typescript
function render({ container }: ComponentFixtureContext): void {
container.classList.add('interactive-session');
// Recreate ancestor structure that CSS selectors expect
const inputPart = dom.$('.interactive-input-part');
const widgetContainer = dom.$('.widget-container');
inputPart.appendChild(widgetContainer);
container.appendChild(inputPart);
widgetContainer.appendChild(myWidget.domNode);
}

Design recommendation for new components: Avoid deeply nested CSS selectors that require specific ancestor elements. Use self-contained class names (e.g., .my-widget .my-element rather than .parent-view .parent-part > .wrapper .my-element). This makes components easier to fixture and reuse.

Services

Using createEditorServices

createEditorServices pre-registers these services: IAccessibilityService, IKeybindingService, IClipboardService, IOpenerService, INotificationService, IDialogService, IUndoRedoService, ILanguageService, IConfigurationService, IStorageService, IThemeService, IModelService, ICodeEditorService, IContextKeyService, ICommandService, ITelemetryService, IHoverService, IUserInteractionService, and more.

Additional services

Register extra services via additionalServices:

typescript
createEditorServices(disposableStore, {
additionalServices: (reg) => {
// Class-based (instantiated by DI):
reg.define(IMyService, MyServiceImpl);
// Instance-based (pre-constructed):
reg.defineInstance(IMyService, myMockInstance);
},
});
Mocking services

Use the mock<T>() helper from base/test/common/mock.js to create mock service instances:

typescript
import { mock } from '../../../../base/test/common/mock.js';
const myService = new class extends mock<IMyService>() {
override someMethod(): string { return 'test'; }
override onSomeEvent = Event.None;
};
reg.defineInstance(IMyService, myService);

For mock view models or data objects:

typescript
const element = new class extends mock<IChatRequestViewModel>() { }();

Async Rendering

The component explorer awaits the render promise, then waits two animation frames. Those frames are not a readiness guarantee for image decoding, native resize observers, or delayed layout.

The render promise must resolve only when the fixture represents its named state:

  • Await assets that affect the screenshot. An editor's setInput() may return before its main image or thumbnails have loaded; wait for the expected image count and await decode(), failing on missing or broken images.
  • Finish layout-changing actions before positioning the viewport. Wait for content height, scroll height, scroll position, and finite animations to settle, then scroll and settle again. Assert that an "offscreen" header is actually offscreen and a "visible" header is not covered by sticky content.
  • Account for transient visuals. A stable height does not mean a stable screenshot. Headless renders never flash auto-hiding scrollbars on scroll or resize, so do not wait for or hide them; use ScrollbarVisibility.Visible when a scrollbar is part of the intended state.
  • Supply deterministic display data through existing services/options, such as a single custom thinking phrase. A seeded random generator alone does not make labels independent of render order or async callback order.
  • Use bounded, condition-based waits that throw on failure, not fixed sleeps or extra frames that silently accept an incomplete state.

Design fixtures as state + local inputs + an explicit readiness condition. Supply local account icons instead of letting a mock fall back to a live avatar URL, and await decoding; waitForFixtureCondition polls native browser frames while preserving the virtual clock. To wait for DOM state, such as an element, class, attribute or text appearing, use waitForFixtureDomCondition, which re-checks the condition after each DOM mutation; a condition that never holds fails at the explorer's fixture timeout. Prefer shared DOM spinners over animated SVG backgrounds: ancestor CSS cannot stop animations inside an image.

Fixtures that depend on native browser image decoding or resize/scroll callbacks can use virtualTime: { enabled: false } and explicitly await those operations. Virtual JavaScript time does not advance the browser's image decoder or layout pipeline.

For async fixtures with paint-order-sensitive edges, deferPaint: true keeps the headless fixture transparent until rendering has finished, without changing its layout or interactive preview. It prevents intermediate paints from affecting the final rasterization; it does not replace awaiting readiness.

Validating fixtures

Do not add automated tests whose subject is fixtures or fixture infrastructure (including fixture-only unit, Playwright, and screenshot-stability tests). Validate fixture changes with the existing Component Explorer rendering, screenshot, stability, and error tools, plus direct inspection or temporary browser probes. Ordinary tests for production behavior belong at the owning production API.

Validate at the same readiness boundary CI uses: capture immediately after the headless renderFixture() resolves, after idle callbacks, and after remounting following another fixture. Compare exact hashes on the same platform and separately inspect the intended state; do not commit the temporary probes as fixture tests.

Pitfall: DOM reparenting causes flickering

Avoid moving rendered widgets between DOM parents after initial render. This causes:

  • Layout recalculation (the widget jumps as position: absolute coordinates become invalid)
  • Focus loss (blur events can trigger hide logic in widgets like QuickInput)
  • Screenshot instability (the component explorer may capture an intermediate layout state)

Bad pattern — reparenting a widget after async wait:

typescript
async function render({ container }: ComponentFixtureContext): Promise<void> {
const host = document.createElement('div');
container.appendChild(host);
// ... create widget inside host ...
await waitForWidget();
container.appendChild(widget); // BAD: reparenting causes flicker
host.remove();
}

Better pattern — render in-place with the correct DOM structure from the start:

typescript
function render({ container }: ComponentFixtureContext): void {
// Set up the correct DOM structure first, then create the widget inside it
const widget = createWidget(container);
container.appendChild(widget.domNode);
}

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files. Certified 10 Mar 2026.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
182,537
Repository forks
38,394
7-day trend score
61

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 16 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill CERTIFIED badge
[![vskill CERTIFIED](https://verified-skill.com/api/v1/skills/microsoft/vscode/component-fixtures/badge)](https://verified-skill.com/skills/microsoft/vscode/component-fixtures)

Something wrong with this skill or its page? Tell us.