add chat response tests, discover and fix a11y issues (#286622)

This commit is contained in:
Megan Rogge 2026-02-03 13:06:14 -06:00 committed by GitHub
parent 1d64e1e1d0
commit 2f87e3fa5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 89 additions and 14 deletions

View File

@ -351,10 +351,10 @@ export class Button extends Disposable implements IButton {
set checked(value: boolean) {
if (value) {
this._element.classList.add('checked');
this._element.setAttribute('aria-checked', 'true');
this._element.setAttribute('aria-pressed', 'true');
} else {
this._element.classList.remove('checked');
this._element.setAttribute('aria-checked', 'false');
this._element.setAttribute('aria-pressed', 'false');
}
}

View File

@ -237,11 +237,11 @@ export class CompositeBarActionViewItem extends BaseActionViewItem {
this.container.classList.add('icon');
}
// Use 'tab' inside tablist, 'button' for popup items outside tablist
const role = this.options.isTabList || !this.options.hasPopup ? 'tab' : 'button';
this.container.setAttribute('role', role);
if (this.options.hasPopup) {
this.container.setAttribute('role', 'button');
this.container.setAttribute('aria-haspopup', 'true');
} else {
this.container.setAttribute('role', 'tab');
}
// Try hard to prevent keyboard only focus feedback when using mouse
@ -479,7 +479,7 @@ export class CompositeOverflowActivityActionViewItem extends CompositeBarActionV
@IConfigurationService configurationService: IConfigurationService,
@IKeybindingService keybindingService: IKeybindingService,
) {
super(action, { icon: true, colors, hasPopup: true, hoverOptions }, () => true, themeService, hoverService, configurationService, keybindingService);
super(action, { icon: true, colors, hasPopup: true, hoverOptions, isTabList: true }, () => true, themeService, hoverService, configurationService, keybindingService);
}
showMenu(): void {

View File

@ -749,7 +749,7 @@ export class AgentTitleBarStatusWidget extends BaseActionViewItem {
// Create dropdown action (empty label prevents default tooltip - we have our own hover)
const dropdownAction = toAction({
id: 'agentStatus.sparkle.dropdown',
label: '',
label: localize('agentStatus.sparkle.dropdown', "More Actions"),
run() { }
});

View File

@ -3,14 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Application, Logger } from '../../../../automation';
import { Application, Logger, Quality } from '../../../../automation';
import { installAllHandlers } from '../../utils';
export function setup(logger: Logger, opts: { web?: boolean }) {
describe.skip('Accessibility', function () {
export function setup(logger: Logger, opts: { web?: boolean }, quality: Quality) {
describe('Accessibility', function () {
// Increase timeout for accessibility scans
this.timeout(30 * 1000);
this.timeout(2 * 60 * 1000);
// Retry tests to minimize flakiness
this.retries(2);
@ -38,7 +38,9 @@ export function setup(logger: Logger, opts: { web?: boolean }) {
// Monaco lists use aria-multiselectable on role="list" and aria-setsize/aria-posinset/aria-selected on role="dialog" rows
// These violations appear intermittently when notification lists or other dynamic lists are visible
// Note: patterns match against HTML string, not CSS selectors, so no leading dots
'aria-allowed-attr': ['monaco-list', 'monaco-list-row']
'aria-allowed-attr': ['monaco-list', 'monaco-list-row'],
// Monaco lists may temporarily contain dialog children during extension activation errors
'aria-required-children': ['monaco-list']
}
});
});
@ -69,7 +71,7 @@ export function setup(logger: Logger, opts: { web?: boolean }) {
});
// Chat is not available in web mode
if (!opts.web) {
if (quality !== Quality.Dev && quality !== Quality.OSS && !opts.web) {
describe('Chat', function () {
it('chat panel has no accessibility violations', async function () {
@ -87,6 +89,79 @@ export function setup(logger: Logger, opts: { web?: boolean }) {
}
});
});
// Chat response test requires gallery service which is only available in non-Dev/OSS builds
it('chat response has no accessibility violations', async function () {
// Disable retries for this test - it modifies settings and retries cause issues
this.retries(0);
// Extend timeout for this test since AI responses can take a while
this.timeout(3 * 60 * 1000);
// Enable anonymous chat access
await app.workbench.settingsEditor.addUserSetting('chat.allowAnonymousAccess', 'true');
// Open chat panel
await app.workbench.quickaccess.runCommand('workbench.action.chat.open');
// Wait for chat view to be visible
await app.workbench.chat.waitForChatView();
// Send a simple message
await app.workbench.chat.sendMessage('Create a simple hello.txt file with the text "Hello World"');
// Wait for the response to complete (1500 retries ~= 150 seconds at 100ms per retry)
await app.workbench.chat.waitForResponse(1500);
// Run accessibility check on the chat panel with the response
await app.code.driver.assertNoAccessibilityViolations({
selector: 'div[id="workbench.panel.chat"]',
excludeRules: {
// Links in chat welcome view show underline on hover/focus which axe-core static analysis cannot detect
'link-in-text-block': ['command:workbench.action.chat.generateInstructions'],
// Monaco lists use aria-multiselectable on role="list" and aria-selected on role="listitem"
// These are used intentionally for selection semantics even though technically not spec-compliant
'aria-allowed-attr': ['monaco-list', 'monaco-list-row'],
// Some icon buttons have empty aria-label during rendering
'aria-command-name': ['codicon-plus']
}
});
});
it('chat terminal tool response has no accessibility violations', async function () {
// Disable retries for this test
this.retries(0);
// Extend timeout for this test since AI responses can take a while
this.timeout(3 * 60 * 1000);
// Enable auto-approve for tools so terminal commands run automatically
await app.workbench.settingsEditor.addUserSetting('chat.tools.global.autoApprove', 'true');
// Open chat panel
await app.workbench.quickaccess.runCommand('workbench.action.chat.open');
// Wait for chat view to be visible
await app.workbench.chat.waitForChatView();
// Send a terminal command request
await app.workbench.chat.sendMessage('Run ls in the terminal');
// Wait for the response to complete (1500 retries ~= 150 seconds at 100ms per retry)
await app.workbench.chat.waitForResponse(1500);
// Run accessibility check on the chat panel with the response
await app.code.driver.assertNoAccessibilityViolations({
selector: 'div[id="workbench.panel.chat"]',
excludeRules: {
// Links in chat welcome view show underline on hover/focus which axe-core static analysis cannot detect
'link-in-text-block': ['command:workbench.action.chat.generateInstructions'],
// Monaco lists use aria-multiselectable on role="list" and aria-selected on role="listitem"
// These are used intentionally for selection semantics even though technically not spec-compliant
'aria-allowed-attr': ['monaco-list', 'monaco-list-row'],
// Some icon buttons have empty aria-label during rendering
'aria-command-name': ['codicon-plus']
}
});
});
});
}
});

View File

@ -408,5 +408,5 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => {
if (!opts.web && !opts.remote) { setupLaunchTests(logger); }
if (!opts.web) { setupChatTests(logger); }
if (!opts.web && quality === Quality.Insiders) { setupChatAnonymousTests(logger); }
setupAccessibilityTests(logger, opts);
setupAccessibilityTests(logger, opts, quality);
});