component-fixtures

bởi microsoft

Sử dụng khi tạo hoặc cập nhật component fixtures cho kiểm thử ảnh chụp màn hình, hoặc khi thiết kế các thành phần UI thân thiện với fixture. Bao gồm cấu trúc tệp fixture,…

npx skills add https://github.com/microsoft/vscode --skill component-fixtures

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

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 and shadow DOM isolation
  • 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:

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:

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 render inside shadow DOM. The component-explorer automatically adopts the global VS Code stylesheets and theme CSS.

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:

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:

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:

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:

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

Async Rendering

The component explorer waits 2 animation frames after the synchronous render function returns. For most components, this is sufficient.

If your render function returns a Promise, the component explorer waits for the promise to resolve.

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:

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:

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);
}

If the component absolutely requires async setup (e.g., QuickInput which renders internally), minimize DOM manipulation after the widget appears by structuring the host container to match the final layout from the beginning.

Adapting Existing Components for Fixtures

Existing components often need small changes to become fixturable. When writing a fixture reveals friction, fix the component — don't work around it in the fixture. Common adaptations:

Decouple CSS from ancestor context

If a component's CSS only works inside a deeply nested selector like .workbench .sidebar .my-view .my-widget, refactor the CSS to be self-contained. Move the styles so they're scoped to the component's own root class:

/* Before: requires specific ancestors */
.workbench .sidebar .my-view .my-widget .header { font-weight: bold; }

/* After: self-contained */
.my-widget .header { font-weight: bold; }

If the component shares styles with its parent (e.g., inheriting background color), use CSS custom properties rather than relying on ancestor selectors.

Extract hard-coded service dependencies

If a component reaches into singletons or global state instead of using DI, refactor it to accept services through the constructor:

// Before: hard to mock in fixtures
class MyWidget {
    private readonly config = getSomeGlobalConfig();
}

// After: injectable and testable
class MyWidget {
    constructor(@IConfigurationService private readonly configService: IConfigurationService) { }
}

Add options to control auto-focus and animation

Components that auto-focus on creation or run animations cause flaky screenshots. Add an options parameter:

interface IMyWidgetOptions {
    shouldAutoFocus?: boolean;
}

The fixture passes shouldAutoFocus: false. The production call site keeps the default behavior.

Expose internal state for "already completed" rendering

Many components have lifecycle states (loading → active → completed). If the component can only reach the "completed" state through user interaction, add support for initializing directly into that state via constructor data:

// The fixture can pass pre-filled data to render the summary/completed state
// without simulating the full user interaction flow.
const carousel: IChatQuestionCarousel = {
    questions,
    allowSkip: true,
    kind: 'questionCarousel',
    isUsed: true,           // Already completed
    data: { 'q1': 'answer' }, // Pre-filled answers
};

Make DOM node accessible

If a component builds its DOM internally and doesn't expose the root element, add a public readonly domNode: HTMLElement property so fixtures can append it to the container.

Keep test-only operations out of the production API

When a fixture or test must drive state that production reaches only through user input or services (for example focusing, selecting, or collapsing rows, or locating a rendered row), don't add public methods for it: other code will start calling them and bypass the real flows. Make the minimum protected and implement the operations in a test-only subclass under test/ (such as TestSessionsList extends SessionsList), which product code cannot import.

Writing Fixture-Friendly Components

When designing new UI components, follow these practices to make them easy to fixture:

1. Accept a container element in the constructor

// Good: container is passed in
class MyWidget {
    constructor(container: HTMLElement, @IFoo foo: IFoo) {
        this.domNode = dom.append(container, dom.$('.my-widget'));
    }
}

// Also good: widget creates its own domNode for the caller to place
class MyWidget {
    readonly domNode: HTMLElement;
    constructor(@IFoo foo: IFoo) {
        this.domNode = dom.$('.my-widget');
    }
}

2. Use dependency injection for all services

All external dependencies should come through DI so fixtures can provide test implementations:

// Good: services injected
constructor(@IThemeService private readonly themeService: IThemeService) { }

// Bad: reaching into globals
constructor() { this.theme = getGlobalTheme(); }

3. Keep CSS selectors shallow

/* Good: self-contained, easy to fixture */
.my-widget .my-header { ... }
.my-widget .my-list-item { ... }

/* Bad: requires deep ancestor chain */
.workbench .sidebar .my-view .my-widget .my-header { ... }

4. Avoid reading from layout/window services during construction

Components that measure the window or read layout dimensions during construction are hard to fixture because the shadow DOM container has different dimensions than the workbench:

// Prefer: use CSS for sizing, or accept dimensions as parameters
container.style.width = '400px';
container.style.height = '300px';

// Avoid: reading from layoutService during construction
const width = this.layoutService.mainContainerDimension.width;

5. Support disabling auto-focus in fixtures

Auto-focus can interfere with screenshot stability. Provide options to disable it:

interface IMyWidgetOptions {
    shouldAutoFocus?: boolean;  // Fixtures pass false
}

6. Expose the DOM node

The fixture needs to append the widget's DOM to the container. Expose it as a public readonly domNode: HTMLElement.

7. Make hover and focus states renderable

Fixtures cannot trigger CSS :hover, and DOM focus is only deterministic through ctx.focus(). Keep interaction-dependent visuals reachable from state:

  • Pair every :hover selector a visual depends on with a .hovered class, e.g. .monaco-list-row:is(:hover, .hovered); :is() keeps specificity unchanged. List widgets already style .monaco-list-row.hovered like a hovered row.
  • Drive focus and selection through the component (list rows get .focused and .selected from the list traits), from a test-only subclass when production has no such API (see Keep test-only operations out of the production API). Then call ctx.focus() when the state needs DOM focus.
  • When JavaScript also reacts to hover (for example mouseover listeners), dispatch a mouseover on the element as well as adding .hovered.

src/vs/sessions/contrib/sessions/test/browser/sessionsListFixtureUtils.ts renders interaction: { hovered, focused, selected } this way.

Multiple Fixture Variants

Create variants to show different states of the same component:

export default defineThemedFixtureGroup({
    // Different data states
    Empty: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { items: [] }) }),
    WithItems: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { items: sampleItems }) }),

    // Different configurations
    ReadOnly: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { readonly: true }) }),
    Editable: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { readonly: false }) }),

    // Lifecycle states
    Loading: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { state: 'loading' }) }),
    Completed: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { state: 'done' }) }),
});

Learnings

Update this section with insights from your fixture development experience!

  • Do not copy the component to the fixture and modify it there. Always adapt the original component to be fixture-friendly, then render it in the fixture. This ensures the fixture tests the real component code and lifecycle, rather than a modified version that may hide bugs.

  • Don't recompose child widgets in fixtures. Never manually instantiate and add a sub-widget (e.g., a toolbar content widget) that the parent component is supposed to create. Instead, configure the parent correctly (e.g., set the right editor option, register the right provider) so the child appears through the normal code path. Manually recomposing hides integration bugs and doesn't test the real widget lifecycle.

  • Describe state as data for service-heavy components. When a component reads many services, give it one harness that takes plain state and renders it through the real state-owning services and production menus, instead of per-fixture mocks and DOM hacks. Fixtures then only list data, and a new feature adds a state field to the harness. The sessions list harness (sessionsListFixtureUtils.ts) follows this pattern; its real toolbars exposed header bugs that stubbed menus had hidden.

  • Never vary process-wide registrations per fixture. The explorer UI mounts all fixtures of a folder at once, and they share command, menu, and other global registries. Register shared actions once, the same for every fixture, and vary per-fixture presentation through the fixture's own services, such as its menu service. Screenshot runs render fixtures one at a time and don't catch mount-order races, so preview the whole folder (___explorer?fixture=<folder>) to check.

Thêm skills từ microsoft

oss-growth
microsoft
Cá tính tăng trưởng OSS
agent-framework-azure-ai-py
microsoft
Xây dựng các tác nhân Azure AI Foundry bằng SDK Python của Microsoft Agent Framework (agent-framework-azure-ai). Sử dụng khi tạo các tác nhân bền vững với AzureAIAgentsProvider, sử dụng các công cụ được lưu trữ (trình thông dịch mã, tìm kiếm tệp, tìm kiếm web), tích hợp máy chủ MCP, quản lý chuỗi hội thoại hoặc triển khai phản hồi phát trực tuyến. Bao gồm các công cụ hàm, đầu ra có cấu trúc và các tác nhân đa công cụ.
development
airunway-aks-setup
microsoft
Thiết lập AI Runway trên AKS — từ cụm trống đến mô hình đang chạy. Bao gồm xác minh cụm, cài đặt controller, đánh giá GPU, thiết lập nhà cung cấp và triển khai đầu tiên. KHI NÀO: "thiết lập AI Runway", "onboard cụm AKS", "cài đặt AI Runway", "thiết lập airunway", "triển khai mô hình lên AKS", "suy luận GPU trên AKS", "thiết lập KAITO trên AKS", "chạy LLM trên AKS", "vLLM trên AKS", "thiết lập phục vụ mô hình trên AKS", "AI Runway controller".
devops
appinsights-instrumentation
microsoft
Hướng dẫn để instrument các ứng dụng web với Azure Application Insights. Cung cấp các mẫu telemetry, thiết lập SDK, và tài liệu tham khảo cấu hình. KHI NÀO: cách instrument ứng dụng, App Insights SDK, các mẫu telemetry, App Insights là gì, hướng dẫn Application Insights, ví dụ instrumentation, các phương pháp tốt nhất APM.
devops
applicationinsights-web-ts
microsoft
Instrument các ứng dụng trình duyệt/web bằng SDK JavaScript Application Insights (@microsoft/applicationinsights-web). Dùng cho Real User Monitoring (RUM) — lượt xem trang, nhấp chuột, phụ thuộc AJAX/fetch, ngoại lệ, sự kiện tùy chỉnh và dấu vết tác nhân GenAI phía trình duyệt tương quan với dấu vết OpenTelemetry phía backend. Bao gồm thiết lập SDK Loader Script và npm, tiện ích mở rộng framework (React, React Native, Angular), Click Analytics, trình khởi tạo telemetry và quy ước ngữ nghĩa OTel GenAI cho các span tác nhân/công cụ/mô hình phát ra từ trình duyệt.
devops
azure-ai-anomalydetector-java
microsoft
Xây dựng ứng dụng phát hiện bất thường với Azure AI Anomaly Detector SDK cho Java. Sử dụng khi triển khai phát hiện bất thường đơn biến/đa biến, phân tích chuỗi thời gian hoặc giám sát hỗ trợ AI.
development
azure-ai-language-conversations-py
microsoft
Triển khai Conversational Language Understanding (CLU) bằng SDK Python azure-ai-language-conversations. Sử dụng khi làm việc với ConversationAnalysisClient để phân tích ý định và thực thể trong hội thoại, xây dựng tính năng NLP, hoặc tích hợp hiểu ngôn ngữ vào ứng dụng.
development
azure-ai-ml-py
microsoft
Azure Machine Learning SDK v2 cho Python. Dùng cho không gian làm việc ML, công việc, mô hình, tập dữ liệu, tính toán và quy trình. Kích hoạt: "azure-ai-ml", "MLClient", "không gian làm việc", "đăng ký mô hình", "công việc đào tạo", "tập dữ liệu".
development