test(ui): 修复彩色输出下的脆弱断言

This commit is contained in:
walker1211
2026-04-13 11:19:16 +08:00
parent 7754c6c0ff
commit 2527889d1d
3 changed files with 67 additions and 32 deletions
+10 -4
View File
@@ -1,6 +1,10 @@
import { describe, expect, it } from 'bun:test';
import { maybeWarnAboutResumeLaneMismatch } from '../../../src/auth/resume-lane-warning';
function stripAnsi(input: string): string {
return input.replace(/\u001b\[[0-9;]*m/g, '');
}
describe('resume lane warning', () => {
it('prints guidance when the plain ccs lane differs from the account lane', async () => {
const logs: string[] = [];
@@ -15,10 +19,12 @@ describe('resume lane warning', () => {
}),
});
expect(logs[0]).toContain('Resume for account "work" will search that account lane');
expect(logs).toContain('[i] Account lane: /tmp/account-lane');
expect(logs).toContain('[i] Plain ccs lane: native Claude lane (/tmp/native-lane)');
expect(logs).toContain('[i] Recover the original lane first: ccs -r');
const plainLogs = logs.map((message) => stripAnsi(message));
expect(plainLogs[0]).toContain('Resume for account "work" will search that account lane');
expect(plainLogs).toContain('[i] Account lane: /tmp/account-lane');
expect(plainLogs).toContain('[i] Plain ccs lane: native Claude lane (/tmp/native-lane)');
expect(plainLogs).toContain('[i] Recover the original lane first: ccs -r');
});
it('does not log anything when resume is not requested', async () => {
@@ -1,5 +1,9 @@
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
function stripAnsi(input: string): string {
return input.replace(/\u001b\[[0-9;]*m/g, '');
}
let calls: string[] = [];
let logLines: string[] = [];
let originalConsoleLog: typeof console.log;
@@ -60,7 +64,7 @@ describe('config-auth command routing', () => {
await handleConfigAuthCommand(['--help']);
expect(calls).toEqual([]);
expect(logLines.join('\n')).toContain('Dashboard Auth Management');
expect(stripAnsi(logLines.join('\n'))).toContain('Dashboard Auth Management');
});
it('rejects trailing arguments for zero-arg subcommands', async () => {
+52 -27
View File
@@ -6,6 +6,25 @@
const assert = require('assert');
function stripAnsi(input) {
return input.replace(/\u001b\[[0-9;]*m/g, '');
}
function withoutForceColor(callback) {
const originalForceColor = process.env.FORCE_COLOR;
delete process.env.FORCE_COLOR;
try {
callback();
} finally {
if (originalForceColor === undefined) {
delete process.env.FORCE_COLOR;
} else {
process.env.FORCE_COLOR = originalForceColor;
}
}
}
describe('UI Module', function () {
let ui;
@@ -25,18 +44,21 @@ describe('UI Module', function () {
});
it('should return plain text when NO_COLOR is set', function () {
const originalNoColor = process.env.NO_COLOR;
process.env.NO_COLOR = '1';
withoutForceColor(() => {
const originalNoColor = process.env.NO_COLOR;
process.env.NO_COLOR = '1';
const result = ui.color('test', 'success');
assert.strictEqual(result, 'test', 'should return unmodified text');
// Restore
if (originalNoColor === undefined) {
delete process.env.NO_COLOR;
} else {
process.env.NO_COLOR = originalNoColor;
}
try {
const result = ui.color('test', 'success');
assert.strictEqual(result, 'test', 'should return unmodified text');
} finally {
if (originalNoColor === undefined) {
delete process.env.NO_COLOR;
} else {
process.env.NO_COLOR = originalNoColor;
}
}
});
});
it('should apply bold formatting', function () {
@@ -51,7 +73,7 @@ describe('UI Module', function () {
it('should apply gradient to text', function () {
const result = ui.gradientText('gradient header');
assert.ok(result.includes('gradient header'), 'should contain original text');
assert.ok(stripAnsi(result).includes('gradient header'), 'should contain original text');
});
});
@@ -129,7 +151,7 @@ describe('UI Module', function () {
describe('Headers', function () {
it('should format section header', function () {
const result = ui.header('Section Title');
assert.ok(result.includes('Section Title'), 'should include title text');
assert.ok(stripAnsi(result).includes('Section Title'), 'should include title text');
});
it('should format subsection header', function () {
@@ -153,21 +175,24 @@ describe('UI Module', function () {
describe('NO_COLOR Compliance', function () {
it('should disable colors when NO_COLOR is set', function () {
const originalNoColor = process.env.NO_COLOR;
process.env.NO_COLOR = '1';
withoutForceColor(() => {
const originalNoColor = process.env.NO_COLOR;
process.env.NO_COLOR = '1';
// All color functions should return plain text
assert.strictEqual(ui.color('text', 'success'), 'text');
assert.strictEqual(ui.bold('text'), 'text');
assert.strictEqual(ui.dim('text'), 'text');
assert.strictEqual(ui.gradientText('text'), 'text');
// Restore
if (originalNoColor === undefined) {
delete process.env.NO_COLOR;
} else {
process.env.NO_COLOR = originalNoColor;
}
try {
// All color functions should return plain text
assert.strictEqual(ui.color('text', 'success'), 'text');
assert.strictEqual(ui.bold('text'), 'text');
assert.strictEqual(ui.dim('text'), 'text');
assert.strictEqual(ui.gradientText('text'), 'text');
} finally {
if (originalNoColor === undefined) {
delete process.env.NO_COLOR;
} else {
process.env.NO_COLOR = originalNoColor;
}
}
});
});
});
});