diff --git a/.github/workflows/screenshot-test.yml b/.github/workflows/screenshot-test.yml index 9c91702bccb..01f186a1c81 100644 --- a/.github/workflows/screenshot-test.yml +++ b/.github/workflows/screenshot-test.yml @@ -54,12 +54,12 @@ jobs: run: npx playwright install chromium - name: Capture screenshots - run: npx component-explorer screenshot --project ./test/componentFixtures/component-explorer.json + run: ./node_modules/.bin/component-explorer screenshot --project ./test/componentFixtures/component-explorer.json - name: Compare screenshots id: compare run: | - npx component-explorer screenshot:compare \ + ./node_modules/.bin/component-explorer screenshot:compare \ --project ./test/componentFixtures \ --report ./test/componentFixtures/.screenshots/report continue-on-error: true @@ -92,19 +92,33 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | REPORT="test/componentFixtures/.screenshots/report/report.json" + STATE="success" if [ -f "$REPORT" ]; then CHANGED=$(node -e "const r = require('./$REPORT'); console.log(r.summary.added + r.summary.removed + r.summary.changed)") TITLE="⚠ ${CHANGED} screenshots changed" + BLOCKS_CI=$(node -e " + const r = require('./$REPORT'); + const blocking = Object.entries(r.fixtures).filter(([, f]) => + f.status !== 'unchanged' && (f.labels || []).includes('blocks-ci') + ); + if (blocking.length > 0) { + console.log(blocking.map(([name]) => name).join(', ')); + } + ") + if [ -n "$BLOCKS_CI" ]; then + STATE="failure" + TITLE="❌ ${CHANGED} screenshots changed (blocks CI: ${BLOCKS_CI})" + fi else TITLE="✅ Screenshots match" fi SHA="${{ github.event.pull_request.head.sha || github.sha }}" - DETAILS_URL="https://hediet-ghartifactpreview.azurewebsites.net/${{ github.repository }}/run/${{ github.run_id }}/component-explorer/___explorer.html?report=./screenshot-report/report.json" + DETAILS_URL="https://hediet-ghartifactpreview.azurewebsites.net/${{ github.repository }}/run/${{ github.run_id }}/component-explorer/___explorer.html?report=./screenshot-report/report.json&search=changed" gh api "repos/${{ github.repository }}/statuses/$SHA" \ --input - < renderCodeEditor(context), }), }); diff --git a/src/vs/workbench/test/browser/componentFixtures/inlineCompletionsExtras.fixture.ts b/src/vs/workbench/test/browser/componentFixtures/inlineCompletionsExtras.fixture.ts index e9298fcf4d1..e06984438ec 100644 --- a/src/vs/workbench/test/browser/componentFixtures/inlineCompletionsExtras.fixture.ts +++ b/src/vs/workbench/test/browser/componentFixtures/inlineCompletionsExtras.fixture.ts @@ -19,6 +19,8 @@ import { InlineCompletionsSource, InlineCompletionsState } from '../../../../edi import { InlineEditItem } from '../../../../editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.js'; import { TextModelValueReference } from '../../../../editor/contrib/inlineCompletions/browser/model/textModelValueReference.js'; import { JumpToView } from '../../../../editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/jumpToView.js'; +import { GutterIndicatorMenuContent } from '../../../../editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorMenu.js'; +import { InlineSuggestionGutterMenuData } from '../../../../editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.js'; import { IUserInteractionService, MockUserInteractionService } from '../../../../platform/userInteraction/browser/userInteractionService.js'; import '../../../../editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.css'; @@ -276,6 +278,58 @@ function createLongDistanceEditor(options: { controller?.model?.get(); } +function renderGutterMenu({ container, disposableStore, theme }: ComponentFixtureContext): void { + container.style.width = '250px'; + container.style.height = '280px'; + + const instantiationService = createEditorServices(disposableStore, { + colorTheme: theme, + additionalServices: (reg) => { + registerWorkbenchServices(reg); + }, + }); + + const textModel = disposableStore.add(createTextModel( + instantiationService, + 'const x = 1;', + URI.parse('inmemory://gutter-menu.ts'), + 'typescript' + )); + + const editor = disposableStore.add(instantiationService.createInstance( + CodeEditorWidget, + document.createElement('div'), + { minimap: { enabled: false } }, + { contributions: [] } satisfies ICodeEditorWidgetOptions + )); + editor.setModel(textModel); + + const editorObs = observableCodeEditor(editor); + const menuData = new InlineSuggestionGutterMenuData( + undefined, + 'Copilot', + [], + undefined, + undefined, + undefined, + ); + + const content = disposableStore.add( + instantiationService.createInstance( + GutterIndicatorMenuContent, + editorObs, + menuData, + () => { }, + ).toDisposableLiveElement() + ); + + container.style.background = 'var(--vscode-editorHoverWidget-background)'; + container.style.border = '2px solid var(--vscode-editorHoverWidget-border)'; + container.style.borderRadius = '3px'; + container.style.color = 'var(--vscode-editorHoverWidget-foreground)'; + container.appendChild(content.element); +} + export default defineThemedFixtureGroup({ path: 'editor/' }, { HintsToolbar: defineComponentFixture({ labels: { kind: 'screenshot' }, @@ -307,4 +361,8 @@ export default defineThemedFixtureGroup({ path: 'editor/' }, { await writeFile(outputPath, processed);`, }), }), + GutterMenu: defineComponentFixture({ + labels: { kind: 'screenshot' }, + render: renderGutterMenu, + }), }); diff --git a/src/vs/workbench/test/browser/componentFixtures/suggestWidget.fixture.ts b/src/vs/workbench/test/browser/componentFixtures/suggestWidget.fixture.ts index a5aada2ea26..8e04b43bc15 100644 --- a/src/vs/workbench/test/browser/componentFixtures/suggestWidget.fixture.ts +++ b/src/vs/workbench/test/browser/componentFixtures/suggestWidget.fixture.ts @@ -34,7 +34,7 @@ interface SuggestFixtureOptions extends ComponentFixtureContext { editorOptions?: IEditorOptions; } -async function renderSuggestWidget(options: SuggestFixtureOptions): Promise { +function renderSuggestWidget(options: SuggestFixtureOptions): void { const { container, disposableStore, theme } = options; container.style.width = options.width ?? '500px'; container.style.height = options.height ?? '300px'; diff --git a/test/componentFixtures/component-explorer-config.schema.json b/test/componentFixtures/component-explorer-config.schema.json deleted file mode 100644 index 3d129dc3b7e..00000000000 --- a/test/componentFixtures/component-explorer-config.schema.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "screenshotDir": { - "description": "Directory for storing screenshots (default: .screenshots)", - "type": "string" - }, - "sessions": { - "minItems": 1, - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Unique session name" - }, - "source": { - "anyOf": [ - { - "type": "string", - "const": "current" - }, - { - "type": "object", - "properties": { - "worktree": { - "type": "object", - "properties": { - "ref": { - "type": "string", - "description": "Git ref (branch, tag, or commit) to check out" - }, - "name": { - "description": "Directory name for the worktree (default: component-explorer-baseline)", - "type": "string" - }, - "install": { - "anyOf": [ - { - "type": "string", - "const": "auto" - }, - { - "type": "string", - "const": "npm" - }, - { - "type": "string", - "const": "pnpm" - }, - { - "type": "string", - "const": "yarn" - }, - { - "type": "object", - "properties": { - "command": { - "type": "string", - "description": "Custom install command to run in the worktree" - } - }, - "required": [ - "command" - ], - "additionalProperties": false - }, - { - "type": "boolean", - "const": false - } - ], - "description": "Dependency install strategy for the worktree" - } - }, - "required": [ - "ref" - ], - "additionalProperties": false, - "description": "Git worktree configuration for a baseline session" - } - }, - "required": [ - "worktree" - ], - "additionalProperties": false - } - ], - "description": "Session source: \"current\" for the working tree, or a worktree config for a baseline" - }, - "viteConfig": { - "description": "Path to vite config file, relative to this config (overrides top-level viteConfig)", - "type": "string" - } - }, - "required": [ - "name" - ], - "additionalProperties": false, - "description": "A component explorer session" - }, - "description": "List of explorer sessions" - }, - "compare": { - "type": "object", - "properties": { - "baseline": { - "type": "string", - "description": "Session name to use as the baseline for comparisons" - }, - "current": { - "type": "string", - "description": "Session name to use as the current version for comparisons" - } - }, - "required": [ - "baseline", - "current" - ], - "additionalProperties": false, - "description": "Screenshot comparison configuration" - }, - "viteConfig": { - "description": "Default vite config file path, relative to this config (default: vite.config.ts)", - "type": "string" - }, - "vite": { - "type": "object", - "properties": { - "hmr": { - "type": "object", - "properties": { - "allowedPaths": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Glob patterns for files that keep HMR; everything else triggers a full reload" - } - }, - "required": [ - "allowedPaths" - ], - "additionalProperties": false, - "description": "Vite HMR configuration" - } - }, - "additionalProperties": false, - "description": "Vite configuration overrides" - }, - "redirection": { - "type": "object", - "properties": { - "port": { - "type": "integer", - "minimum": 1, - "maximum": 65535, - "description": "Port for the redirection HTTP server" - }, - "host": { - "description": "Host to bind the redirection server to (default: localhost)", - "type": "string" - } - }, - "required": [ - "port" - ], - "additionalProperties": false, - "description": "HTTP redirection server that redirects to the session URL" - }, - "$schema": { - "type": "string", - "description": "URL of the JSON Schema for this config file" - } - }, - "required": [ - "sessions" - ], - "additionalProperties": false, - "description": "Component Explorer configuration" -} diff --git a/test/componentFixtures/component-explorer.json b/test/componentFixtures/component-explorer.json index 2f24a100b02..cc0a4596269 100644 --- a/test/componentFixtures/component-explorer.json +++ b/test/componentFixtures/component-explorer.json @@ -1,11 +1,17 @@ { - "$schema": "./component-explorer-config.schema.json", + "$schema": "../../node_modules/@vscode/component-explorer-cli/dist/component-explorer-config.schema.json", "screenshotDir": ".screenshots", "sessions": [ { "name": "current" } ], + "worktree": { + "maxSlots": 1, + "setup": { + "command": "echo 'test'" + } + }, "redirection": { "port": 5337 },