mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 02:14:32 +00:00
test(ui): 修复彩色输出下的脆弱断言
This commit is contained in:
1 parent
7754c6c0ff
commit
2527889d1d
3 files changed
+67
-32
No files matched your search
@@ -1,6 +1,10 @@
|
|||||||
import { describe, expect, it } from 'bun:test';
|
import { describe, expect, it } from 'bun:test';
|
||||||
import { maybeWarnAboutResumeLaneMismatch } from '../../../src/auth/resume-lane-warning';
|
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', () => {
|
describe('resume lane warning', () => {
|
||||||
it('prints guidance when the plain ccs lane differs from the account lane', async () => {
|
it('prints guidance when the plain ccs lane differs from the account lane', async () => {
|
||||||
const logs: string[] = [];
|
const logs: string[] = [];
|
||||||
@@ -15,10 +19,12 @@ describe('resume lane warning', () => {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(logs[0]).toContain('Resume for account "work" will search that account lane');
|
const plainLogs = logs.map((message) => stripAnsi(message));
|
||||||
expect(logs).toContain('[i] Account lane: /tmp/account-lane');
|
|
||||||
expect(logs).toContain('[i] Plain ccs lane: native Claude lane (/tmp/native-lane)');
|
expect(plainLogs[0]).toContain('Resume for account "work" will search that account lane');
|
||||||
expect(logs).toContain('[i] Recover the original lane first: ccs -r');
|
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 () => {
|
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';
|
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 calls: string[] = [];
|
||||||
let logLines: string[] = [];
|
let logLines: string[] = [];
|
||||||
let originalConsoleLog: typeof console.log;
|
let originalConsoleLog: typeof console.log;
|
||||||
@@ -60,7 +64,7 @@ describe('config-auth command routing', () => {
|
|||||||
await handleConfigAuthCommand(['--help']);
|
await handleConfigAuthCommand(['--help']);
|
||||||
|
|
||||||
expect(calls).toEqual([]);
|
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 () => {
|
it('rejects trailing arguments for zero-arg subcommands', async () => {
|
||||||
|
|||||||
+52
-27
@@ -6,6 +6,25 @@
|
|||||||
|
|
||||||
const assert = require('assert');
|
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 () {
|
describe('UI Module', function () {
|
||||||
let ui;
|
let ui;
|
||||||
|
|
||||||
@@ -25,18 +44,21 @@ describe('UI Module', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return plain text when NO_COLOR is set', function () {
|
it('should return plain text when NO_COLOR is set', function () {
|
||||||
const originalNoColor = process.env.NO_COLOR;
|
withoutForceColor(() => {
|
||||||
process.env.NO_COLOR = '1';
|
const originalNoColor = process.env.NO_COLOR;
|
||||||
|
process.env.NO_COLOR = '1';
|
||||||
|
|
||||||
const result = ui.color('test', 'success');
|
try {
|
||||||
assert.strictEqual(result, 'test', 'should return unmodified text');
|
const result = ui.color('test', 'success');
|
||||||
|
assert.strictEqual(result, 'test', 'should return unmodified text');
|
||||||
// Restore
|
} finally {
|
||||||
if (originalNoColor === undefined) {
|
if (originalNoColor === undefined) {
|
||||||
delete process.env.NO_COLOR;
|
delete process.env.NO_COLOR;
|
||||||
} else {
|
} else {
|
||||||
process.env.NO_COLOR = originalNoColor;
|
process.env.NO_COLOR = originalNoColor;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply bold formatting', function () {
|
it('should apply bold formatting', function () {
|
||||||
@@ -51,7 +73,7 @@ describe('UI Module', function () {
|
|||||||
|
|
||||||
it('should apply gradient to text', function () {
|
it('should apply gradient to text', function () {
|
||||||
const result = ui.gradientText('gradient header');
|
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 () {
|
describe('Headers', function () {
|
||||||
it('should format section header', function () {
|
it('should format section header', function () {
|
||||||
const result = ui.header('Section Title');
|
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 () {
|
it('should format subsection header', function () {
|
||||||
@@ -153,21 +175,24 @@ describe('UI Module', function () {
|
|||||||
|
|
||||||
describe('NO_COLOR Compliance', function () {
|
describe('NO_COLOR Compliance', function () {
|
||||||
it('should disable colors when NO_COLOR is set', function () {
|
it('should disable colors when NO_COLOR is set', function () {
|
||||||
const originalNoColor = process.env.NO_COLOR;
|
withoutForceColor(() => {
|
||||||
process.env.NO_COLOR = '1';
|
const originalNoColor = process.env.NO_COLOR;
|
||||||
|
process.env.NO_COLOR = '1';
|
||||||
|
|
||||||
// All color functions should return plain text
|
try {
|
||||||
assert.strictEqual(ui.color('text', 'success'), 'text');
|
// All color functions should return plain text
|
||||||
assert.strictEqual(ui.bold('text'), 'text');
|
assert.strictEqual(ui.color('text', 'success'), 'text');
|
||||||
assert.strictEqual(ui.dim('text'), 'text');
|
assert.strictEqual(ui.bold('text'), 'text');
|
||||||
assert.strictEqual(ui.gradientText('text'), 'text');
|
assert.strictEqual(ui.dim('text'), 'text');
|
||||||
|
assert.strictEqual(ui.gradientText('text'), 'text');
|
||||||
// Restore
|
} finally {
|
||||||
if (originalNoColor === undefined) {
|
if (originalNoColor === undefined) {
|
||||||
delete process.env.NO_COLOR;
|
delete process.env.NO_COLOR;
|
||||||
} else {
|
} else {
|
||||||
process.env.NO_COLOR = originalNoColor;
|
process.env.NO_COLOR = originalNoColor;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user