// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. /** * Types and helper utilities for chart gallery test cases. * No React/UI dependencies — pure TypeScript. */ import { Type } from './df-types'; import { Channel, EncodingItem, FieldItem } from './df-types'; import { AssembleOptions } from '../core/types'; import type { SemanticAnnotation } from '../core/field-semantics'; // ============================================================================ // Test Case Definition // ============================================================================ export interface TestCase { title: string; description: string; tags: string[]; // e.g., ['temporal', 'large-cardinality', 'color'] chartType: string; data: Record[]; fields: FieldItem[]; metadata: Record; encodingMap: Partial>; chartProperties?: Record; assembleOptions?: AssembleOptions; /** * Enriched semantic annotations that override the plain semanticType strings * in metadata. Use this when a field needs extra info like intrinsicDomain or unit. * E.g., { rating: { semanticType: 'Score', intrinsicDomain: [1, 5] } } */ semanticAnnotations?: Record; } /** Date format definition for date stress tests */ export interface DateFormat { label: string; description: string; values: any[]; fieldName: string; expectedType: Type; semanticType: string; } // ============================================================================ // Helper Functions // ============================================================================ export function makeField(name: string, tableRef = 'test'): FieldItem { return { id: name, name, source: 'original', tableRef }; } export function makeEncodingItem(fieldID: string, opts?: Partial): EncodingItem { return { fieldID, ...opts }; } export function inferType(values: any[]): Type { if (values.length === 0) return Type.String; const sample = values.find(v => v != null); if (typeof sample === 'number') return Type.Number; if (typeof sample === 'boolean') return Type.String; if (sample instanceof Date) return Type.Date; // Check if string looks like a date if (typeof sample === 'string' && !isNaN(Date.parse(sample)) && sample.length > 4) return Type.Date; return Type.String; } export function buildMetadata(data: Record[]): Record { if (data.length === 0) return {}; const meta: Record = {}; for (const key of Object.keys(data[0])) { const values = data.map(r => r[key]).filter(v => v != null); const type = inferType(values); const levels = [...new Set(values)]; // Assign semantic types heuristically let semanticType = ''; if (type === Type.Date || type === Type.DateTime || type === Type.Time) semanticType = 'Date'; else if (type === Type.Number || type === Type.Duration) semanticType = 'Quantity'; else semanticType = 'Category'; meta[key] = { type, semanticType, levels }; } return meta; }