chore: import upstream snapshot with attribution
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
import chalk from 'chalk';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { TERMINAL_MAX_WIDTH } from '../src/constants';
|
||||
import { generateTable, wrapTable } from '../src/table';
|
||||
import { type EvaluateTable, ResultFailureReason } from '../src/types/index';
|
||||
import {
|
||||
createCompletedPrompt,
|
||||
createEvaluateTable,
|
||||
createEvaluateTableOutput,
|
||||
createEvaluateTableRow,
|
||||
} from './factories/eval';
|
||||
|
||||
// Track all created instances and constructor calls
|
||||
const mockTableInstances: any[] = [];
|
||||
const mockConstructorCalls: any[] = [];
|
||||
|
||||
// Create a hoisted mock class for Table - must be an actual class to work with `new`
|
||||
const MockTable = vi.hoisted(() => {
|
||||
return class MockTable {
|
||||
push: ReturnType<typeof vi.fn>;
|
||||
toString: ReturnType<typeof vi.fn>;
|
||||
options: object;
|
||||
width: number;
|
||||
length: number;
|
||||
pop: ReturnType<typeof vi.fn>;
|
||||
|
||||
constructor(options: any) {
|
||||
mockConstructorCalls.push(options);
|
||||
this.push = vi.fn();
|
||||
this.toString = vi.fn().mockReturnValue('mocked table string');
|
||||
this.options = options || {};
|
||||
this.width = 0;
|
||||
this.length = 0;
|
||||
this.pop = vi.fn();
|
||||
mockTableInstances.push(this);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('cli-table3', () => ({
|
||||
default: MockTable,
|
||||
}));
|
||||
|
||||
describe('table', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
mockTableInstances.length = 0;
|
||||
mockConstructorCalls.length = 0;
|
||||
});
|
||||
|
||||
describe('generateTable', () => {
|
||||
const mockEvaluateTable: EvaluateTable = createEvaluateTable({
|
||||
head: {
|
||||
vars: ['var1', 'var2'],
|
||||
prompts: [
|
||||
createCompletedPrompt('test prompt', {
|
||||
provider: 'test-provider',
|
||||
label: 'test-label',
|
||||
id: 'test-id',
|
||||
display: 'test display',
|
||||
config: {},
|
||||
metrics: undefined,
|
||||
}),
|
||||
],
|
||||
},
|
||||
body: [
|
||||
createEvaluateTableRow({
|
||||
vars: ['value1', 'value2'],
|
||||
outputs: [createEvaluateTableOutput({ text: 'passing test' })],
|
||||
testIdx: 0,
|
||||
}),
|
||||
createEvaluateTableRow({
|
||||
vars: ['value3', 'value4'],
|
||||
outputs: [
|
||||
createEvaluateTableOutput({
|
||||
pass: false,
|
||||
score: 0,
|
||||
text: 'failing test',
|
||||
failureReason: ResultFailureReason.ASSERT,
|
||||
}),
|
||||
],
|
||||
testIdx: 1,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
it('should generate table with correct headers', () => {
|
||||
generateTable(mockEvaluateTable);
|
||||
|
||||
expect(mockConstructorCalls[0]).toEqual({
|
||||
head: ['var1', 'var2', '[test-provider] test-label'],
|
||||
colWidths: expect.any(Array),
|
||||
wordWrap: true,
|
||||
wrapOnWordBoundary: true,
|
||||
style: { head: ['blue', 'bold'] },
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle passing and failing rows correctly', () => {
|
||||
generateTable(mockEvaluateTable);
|
||||
|
||||
const table = mockTableInstances[0];
|
||||
expect(table.push).toHaveBeenCalledWith([
|
||||
'value1',
|
||||
'value2',
|
||||
chalk.green('[PASS] ') + 'passing test',
|
||||
]);
|
||||
|
||||
expect(table.push).toHaveBeenCalledWith([
|
||||
'value3',
|
||||
'value4',
|
||||
chalk.red('[FAIL] ') + chalk.red.bold('failing test'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should respect maxRows parameter', () => {
|
||||
generateTable(mockEvaluateTable, 250, 1);
|
||||
|
||||
const table = mockTableInstances[0];
|
||||
expect(table.push).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should respect tableCellMaxLength parameter', () => {
|
||||
const longText = 'a'.repeat(300);
|
||||
const shortMaxLength = 10;
|
||||
|
||||
const testTable: EvaluateTable = createEvaluateTable({
|
||||
head: { vars: [longText], prompts: [] },
|
||||
body: [
|
||||
createEvaluateTableRow({
|
||||
vars: [longText],
|
||||
outputs: [createEvaluateTableOutput({ text: 'test' })],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
generateTable(testTable, shortMaxLength);
|
||||
|
||||
expect(mockConstructorCalls[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
head: [expect.stringMatching(/^a{7}\.{3}$/)],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('wrapTable', () => {
|
||||
it('should return message when no rows provided', () => {
|
||||
const result = wrapTable([]);
|
||||
expect(result).toBe('No data to display');
|
||||
});
|
||||
|
||||
it('should create table with correct headers and data', () => {
|
||||
const rows = [
|
||||
{ col1: 'value1', col2: 'value2' },
|
||||
{ col1: 'value3', col2: 'value4' },
|
||||
];
|
||||
|
||||
wrapTable(rows);
|
||||
|
||||
expect(mockConstructorCalls[0]).toEqual({
|
||||
head: ['col1', 'col2'],
|
||||
colWidths: expect.any(Array),
|
||||
wordWrap: true,
|
||||
wrapOnWordBoundary: true,
|
||||
});
|
||||
|
||||
const table = mockTableInstances[0];
|
||||
expect(table.push).toHaveBeenCalledWith(['value1', 'value2']);
|
||||
expect(table.push).toHaveBeenCalledWith(['value3', 'value4']);
|
||||
});
|
||||
|
||||
it('should create table with custom column widths', () => {
|
||||
const rows = [
|
||||
{ col1: 'value1', col2: 'value2', col3: 'value3' },
|
||||
{ col1: 'value4', col2: 'value5', col3: 'value6' },
|
||||
];
|
||||
const columnWidths = {
|
||||
col1: 10,
|
||||
col2: 20,
|
||||
col3: 15,
|
||||
};
|
||||
|
||||
wrapTable(rows, columnWidths);
|
||||
|
||||
expect(mockConstructorCalls[0]).toEqual({
|
||||
head: ['col1', 'col2', 'col3'],
|
||||
colWidths: [10, 20, 15],
|
||||
wordWrap: true,
|
||||
wrapOnWordBoundary: true,
|
||||
});
|
||||
|
||||
const table = mockTableInstances[0];
|
||||
expect(table.push).toHaveBeenCalledWith(['value1', 'value2', 'value3']);
|
||||
expect(table.push).toHaveBeenCalledWith(['value4', 'value5', 'value6']);
|
||||
});
|
||||
|
||||
it('should use default width for columns not specified in columnWidths', () => {
|
||||
const rows = [{ col1: 'value1', col2: 'value2', col3: 'value3' }];
|
||||
const columnWidths = {
|
||||
col1: 10,
|
||||
// col2 not specified
|
||||
col3: 15,
|
||||
};
|
||||
|
||||
wrapTable(rows, columnWidths);
|
||||
|
||||
const defaultWidth = Math.floor(TERMINAL_MAX_WIDTH / 3); // 3 columns
|
||||
expect(mockConstructorCalls[0]).toEqual({
|
||||
head: ['col1', 'col2', 'col3'],
|
||||
colWidths: [10, defaultWidth, 15],
|
||||
wordWrap: true,
|
||||
wrapOnWordBoundary: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a string representation of the table', () => {
|
||||
const rows = [
|
||||
{ col1: 'value1', col2: 'value2' },
|
||||
{ col1: 'value3', col2: 'value4' },
|
||||
];
|
||||
|
||||
const result = wrapTable(rows);
|
||||
|
||||
expect(typeof result).toBe('string');
|
||||
expect(result).toBe('mocked table string');
|
||||
const table = mockTableInstances[0];
|
||||
expect(table.toString).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return string for single row', () => {
|
||||
const rows = [{ name: 'John', age: 30 }];
|
||||
|
||||
const result = wrapTable(rows);
|
||||
|
||||
expect(typeof result).toBe('string');
|
||||
expect(result).toBe('mocked table string');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user