Just a whole lot of crap

This commit is contained in:
2025-08-14 16:36:12 -05:00
parent 66b7e42275
commit 62f6d6f279
144 changed files with 41421 additions and 2458 deletions

View File

@@ -0,0 +1,507 @@
/**
* Accessibility Tests
* Tests for screen reader support, high contrast mode, and focus indicators
* Requirements: 8.1, 8.2, 8.3
*/
const React = require("react");
const { render } = require("ink-testing-library");
const {
AccessibilityConfig,
ScreenReaderUtils,
getAccessibleColors,
FocusManager,
KeyboardNavigation,
AccessibilityAnnouncer,
} = require("../../src/tui/utils/accessibility.js");
// Mock environment variables for testing
const mockEnv = (envVars) => {
const originalEnv = { ...process.env };
Object.assign(process.env, envVars);
return () => {
process.env = originalEnv;
};
};
describe("Accessibility Configuration", () => {
describe("Screen Reader Detection", () => {
test("should detect screen reader when NVDA_ACTIVE is true", () => {
const restore = mockEnv({ NVDA_ACTIVE: "true" });
expect(AccessibilityConfig.isScreenReaderActive()).toBe(true);
restore();
});
test("should detect screen reader when JAWS_ACTIVE is true", () => {
const restore = mockEnv({ JAWS_ACTIVE: "true" });
expect(AccessibilityConfig.isScreenReaderActive()).toBe(true);
restore();
});
test("should detect screen reader when SCREEN_READER is true", () => {
const restore = mockEnv({ SCREEN_READER: "true" });
expect(AccessibilityConfig.isScreenReaderActive()).toBe(true);
restore();
});
test("should detect screen reader when ACCESSIBILITY_MODE is true", () => {
const restore = mockEnv({ ACCESSIBILITY_MODE: "true" });
expect(AccessibilityConfig.isScreenReaderActive()).toBe(true);
restore();
});
test("should not detect screen reader when no variables are set", () => {
const restore = mockEnv({
NVDA_ACTIVE: undefined,
JAWS_ACTIVE: undefined,
SCREEN_READER: undefined,
ACCESSIBILITY_MODE: undefined,
});
expect(AccessibilityConfig.isScreenReaderActive()).toBe(false);
restore();
});
});
describe("High Contrast Mode Detection", () => {
test("should detect high contrast when HIGH_CONTRAST_MODE is true", () => {
const restore = mockEnv({ HIGH_CONTRAST_MODE: "true" });
expect(AccessibilityConfig.isHighContrastMode()).toBe(true);
restore();
});
test("should detect high contrast when FORCE_HIGH_CONTRAST is true", () => {
const restore = mockEnv({ FORCE_HIGH_CONTRAST: "true" });
expect(AccessibilityConfig.isHighContrastMode()).toBe(true);
restore();
});
test("should not detect high contrast when no variables are set", () => {
const restore = mockEnv({
HIGH_CONTRAST_MODE: undefined,
FORCE_HIGH_CONTRAST: undefined,
});
expect(AccessibilityConfig.isHighContrastMode()).toBe(false);
restore();
});
});
describe("Enhanced Focus Detection", () => {
test("should show enhanced focus when screen reader is active", () => {
const restore = mockEnv({ SCREEN_READER: "true" });
expect(AccessibilityConfig.shouldShowEnhancedFocus()).toBe(true);
restore();
});
test("should show enhanced focus when ENHANCED_FOCUS is true", () => {
const restore = mockEnv({ ENHANCED_FOCUS: "true" });
expect(AccessibilityConfig.shouldShowEnhancedFocus()).toBe(true);
restore();
});
test("should not show enhanced focus when no conditions are met", () => {
const restore = mockEnv({
SCREEN_READER: undefined,
ENHANCED_FOCUS: undefined,
NVDA_ACTIVE: undefined,
JAWS_ACTIVE: undefined,
ACCESSIBILITY_MODE: undefined,
});
expect(AccessibilityConfig.shouldShowEnhancedFocus()).toBe(false);
restore();
});
});
describe("Reduced Motion Detection", () => {
test("should detect reduced motion preference", () => {
const restore = mockEnv({ PREFERS_REDUCED_MOTION: "true" });
expect(AccessibilityConfig.prefersReducedMotion()).toBe(true);
restore();
});
test("should not detect reduced motion when not set", () => {
const restore = mockEnv({ PREFERS_REDUCED_MOTION: undefined });
expect(AccessibilityConfig.prefersReducedMotion()).toBe(false);
restore();
});
});
});
describe("Screen Reader Utils", () => {
describe("Menu Item Description", () => {
test("should generate correct description for simple menu item", () => {
const item = { label: "Configuration", description: "Edit settings" };
const description = ScreenReaderUtils.describeMenuItem(item, 0, 3, true);
expect(description).toBe(
"Configuration, Edit settings, Item 1 of 3, selected"
);
});
test("should generate correct description without description field", () => {
const item = { label: "Main Menu" };
const description = ScreenReaderUtils.describeMenuItem(item, 1, 3, false);
expect(description).toBe("Main Menu, Item 2 of 3, not selected");
});
test("should handle different index positions", () => {
const item = { label: "Logs" };
const description = ScreenReaderUtils.describeMenuItem(item, 2, 3, false);
expect(description).toBe("Logs, Item 3 of 3, not selected");
});
});
describe("Progress Description", () => {
test("should generate correct progress description", () => {
const description = ScreenReaderUtils.describeProgress(
25,
100,
"Processing products"
);
expect(description).toBe(
"Processing products: 25 of 100 complete, 25 percent"
);
});
test("should handle zero progress", () => {
const description = ScreenReaderUtils.describeProgress(
0,
50,
"Starting operation"
);
expect(description).toBe(
"Starting operation: 0 of 50 complete, 0 percent"
);
});
test("should handle complete progress", () => {
const description = ScreenReaderUtils.describeProgress(
100,
100,
"Operation complete"
);
expect(description).toBe(
"Operation complete: 100 of 100 complete, 100 percent"
);
});
});
describe("Status Description", () => {
test("should generate status description with details", () => {
const description = ScreenReaderUtils.describeStatus(
"connected",
"API rate limit: 40/40"
);
expect(description).toBe("Connected to Shopify, API rate limit: 40/40");
});
test("should generate status description without details", () => {
const description = ScreenReaderUtils.describeStatus("error");
expect(description).toBe("Error occurred");
});
test("should handle unknown status", () => {
const description = ScreenReaderUtils.describeStatus("custom_status");
expect(description).toBe("custom_status");
});
});
describe("Form Field Description", () => {
test("should describe valid form field with value", () => {
const description = ScreenReaderUtils.describeFormField(
"Shop Domain",
"mystore.myshopify.com",
true,
null
);
expect(description).toBe(
"Shop Domain, current value: mystore.myshopify.com, valid"
);
});
test("should describe invalid form field with error", () => {
const description = ScreenReaderUtils.describeFormField(
"Access Token",
"invalid_token",
false,
"Token format is incorrect"
);
expect(description).toBe(
"Access Token, current value: invalid_token, invalid, Token format is incorrect"
);
});
test("should describe empty form field", () => {
const description = ScreenReaderUtils.describeFormField(
"Target Tag",
"",
true,
null
);
expect(description).toBe("Target Tag, no value entered, valid");
});
});
});
describe("Accessible Colors", () => {
describe("Normal Mode Colors", () => {
test("should return standard colors when high contrast is disabled", () => {
const restore = mockEnv({ HIGH_CONTRAST_MODE: undefined });
const colors = getAccessibleColors();
expect(colors.accent).toBe("blue");
expect(colors.success).toBe("green");
expect(colors.error).toBe("red");
expect(colors.warning).toBe("yellow");
expect(colors.focus).toBe("blue");
expect(colors.selection).toBe("blue");
restore();
});
});
describe("High Contrast Mode Colors", () => {
test("should return high contrast colors when enabled", () => {
const restore = mockEnv({ HIGH_CONTRAST_MODE: "true" });
const colors = getAccessibleColors();
expect(colors.background).toBe("black");
expect(colors.foreground).toBe("white");
expect(colors.accent).toBe("yellow");
expect(colors.focus).toBe("yellow");
expect(colors.selection).toBe("white");
restore();
});
test("should use alternative high contrast scheme when specified", () => {
const restore = mockEnv({
HIGH_CONTRAST_MODE: "true",
HIGH_CONTRAST_SCHEME: "alternative",
});
const colors = getAccessibleColors();
expect(colors.background).toBe("white");
expect(colors.foreground).toBe("black");
expect(colors.accent).toBe("blue");
expect(colors.focus).toBe("blue");
expect(colors.selection).toBe("black");
restore();
});
});
});
describe("Focus Manager", () => {
describe("Focus Props Generation", () => {
test("should generate standard focus props when not focused", () => {
const props = FocusManager.getFocusProps(false);
expect(props).toEqual({
borderStyle: "single",
borderColor: "gray",
});
});
test("should generate enhanced focus props when accessibility is enabled", () => {
const restore = mockEnv({ ENHANCED_FOCUS: "true" });
const props = FocusManager.getFocusProps(true, "input");
expect(props.borderStyle).toBe("double");
expect(props.borderColor).toBeDefined();
// backgroundColor may be undefined for non-input components in normal mode
expect(props).toHaveProperty("backgroundColor");
restore();
});
test("should generate standard focus props when accessibility is disabled", () => {
const restore = mockEnv({ ENHANCED_FOCUS: undefined });
const props = FocusManager.getFocusProps(true);
expect(props.borderStyle).toBe("single");
expect(props.borderColor).toBeDefined();
restore();
});
});
describe("Selection Props Generation", () => {
test("should generate selection props when selected", () => {
const props = FocusManager.getSelectionProps(true);
expect(props.bold).toBe(true);
expect(props.color).toBeDefined();
});
test("should generate normal props when not selected", () => {
const props = FocusManager.getSelectionProps(false);
// color may be undefined in normal mode (uses terminal default)
expect(props).toHaveProperty("color");
expect(props.bold).toBeUndefined();
});
test("should include background color in high contrast mode", () => {
const restore = mockEnv({ HIGH_CONTRAST_MODE: "true" });
const props = FocusManager.getSelectionProps(true);
expect(props.backgroundColor).toBeDefined();
expect(props.bold).toBe(true);
restore();
});
});
});
describe("Keyboard Navigation", () => {
describe("Navigation Key Detection", () => {
test("should detect up arrow key", () => {
expect(KeyboardNavigation.isNavigationKey({ name: "up" }, "up")).toBe(
true
);
});
test("should detect vim-style up key", () => {
expect(KeyboardNavigation.isNavigationKey({ name: "k" }, "up")).toBe(
true
);
});
test("should detect enter key for selection", () => {
expect(
KeyboardNavigation.isNavigationKey({ name: "return" }, "select")
).toBe(true);
});
test("should detect space key for selection", () => {
expect(
KeyboardNavigation.isNavigationKey({ name: "space" }, "select")
).toBe(true);
});
test("should detect ctrl+c for quit", () => {
expect(
KeyboardNavigation.isNavigationKey({ ctrl: true, name: "c" }, "quit")
).toBe(true);
});
test("should not detect incorrect key combinations", () => {
expect(KeyboardNavigation.isNavigationKey({ name: "a" }, "up")).toBe(
false
);
});
});
describe("Shortcut Descriptions", () => {
test("should generate description for single action", () => {
const description = KeyboardNavigation.describeShortcuts(["up"]);
expect(description).toBe("Up arrow or K to move up");
});
test("should generate description for multiple actions", () => {
const description = KeyboardNavigation.describeShortcuts([
"up",
"down",
"select",
]);
expect(description).toContain("Up arrow or K to move up");
expect(description).toContain("Down arrow or J to move down");
expect(description).toContain("Enter or Space to select");
});
test("should handle empty actions array", () => {
const description = KeyboardNavigation.describeShortcuts([]);
expect(description).toBe("");
});
test("should handle unknown actions", () => {
const description = KeyboardNavigation.describeShortcuts([
"unknown_action",
]);
expect(description).toBe("");
});
});
});
describe("Accessibility Announcer", () => {
let originalConsoleLog;
beforeEach(() => {
originalConsoleLog = console.log;
console.log = jest.fn();
AccessibilityAnnouncer.announcements = [];
});
afterEach(() => {
console.log = originalConsoleLog;
});
describe("Announcement Queue", () => {
test("should add announcement to queue when screen reader is active", () => {
const restore = mockEnv({
SCREEN_READER: "true",
NODE_ENV: "development",
});
AccessibilityAnnouncer.announce("Test message", "polite");
expect(AccessibilityAnnouncer.announcements).toHaveLength(1);
expect(AccessibilityAnnouncer.announcements[0].message).toBe(
"Test message"
);
expect(AccessibilityAnnouncer.announcements[0].priority).toBe("polite");
restore();
});
test("should not add announcement when screen reader is inactive", () => {
const restore = mockEnv({ SCREEN_READER: undefined });
AccessibilityAnnouncer.announce("Test message");
expect(AccessibilityAnnouncer.announcements).toHaveLength(0);
restore();
});
test("should log announcement in development mode", () => {
const restore = mockEnv({
SCREEN_READER: "true",
NODE_ENV: "development",
});
AccessibilityAnnouncer.announce("Test message", "assertive");
expect(console.log).toHaveBeenCalledWith(
"[SCREEN_READER_ASSERTIVE]: Test message"
);
restore();
});
});
describe("Announcement Cleanup", () => {
test("should clear old announcements", () => {
const restore = mockEnv({ SCREEN_READER: "true" });
// Add old announcement
AccessibilityAnnouncer.announcements.push({
message: "Old message",
priority: "polite",
timestamp: Date.now() - 10000, // 10 seconds ago
});
// Add recent announcement
AccessibilityAnnouncer.announcements.push({
message: "Recent message",
priority: "polite",
timestamp: Date.now(),
});
AccessibilityAnnouncer.clearOldAnnouncements(5000); // 5 second max age
expect(AccessibilityAnnouncer.announcements).toHaveLength(1);
expect(AccessibilityAnnouncer.announcements[0].message).toBe(
"Recent message"
);
restore();
});
});
});