diff --git a/docs/plans/2026-03-08-utils-coverage-implementation.md b/docs/plans/2026-03-08-utils-coverage-implementation.md
new file mode 100644
index 0000000..fea2cc4
--- /dev/null
+++ b/docs/plans/2026-03-08-utils-coverage-implementation.md
@@ -0,0 +1,50 @@
+# Utils Coverage Implementation Plan
+
+> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
+
+**Goal:** Add missing unit tests so the current `vitest` coverage scope for `src/**` reaches 100% without changing production code.
+
+**Architecture:** Keep all changes in the existing `test/Truncate.spec.tsx` suite. Target uncovered branches in `src/Truncate/utils.tsx`, especially the expansion, proportional truncation, and final fine-tuning paths inside `getMiddleTruncateFragments`.
+
+**Tech Stack:** React, Vitest, Testing Library, Sinon
+
+---
+
+### Task 1: Add failing coverage tests for `getMiddleTruncateFragments`
+
+**Files:**
+- Modify: `test/Truncate.spec.tsx`
+- Test: `test/Truncate.spec.tsx`
+
+**Step 1: Write the failing test**
+
+Add focused test cases for:
+- expanding `startFragment` before hitting `targetWidth`
+- expanding `endFragment` when more width remains
+- truncating `endFragment` when it is proportionally wider
+- truncating only one side after the opposite side becomes empty
+- fine-tuning with one extra character added to `startFragment`
+- fine-tuning with one extra character added to `endFragment`
+
+**Step 2: Run test to verify expected behavior**
+
+Run: `pnpm test test/Truncate.spec.tsx`
+
+Expected: the new cases either pass immediately because behavior already exists, or fail with assertion output that identifies the remaining uncovered path.
+
+### Task 2: Verify full coverage
+
+**Files:**
+- Test: `test/Truncate.spec.tsx`
+
+**Step 1: Run the targeted suite**
+
+Run: `pnpm test test/Truncate.spec.tsx`
+
+Expected: all tests pass.
+
+**Step 2: Run coverage**
+
+Run: `pnpm coverage`
+
+Expected: `src/**` coverage reaches 100% for statements, branches, functions, and lines. If any path remains uncovered, add one more focused test and re-run coverage.
diff --git a/src/Truncate/utils.tsx b/src/Truncate/utils.tsx
index 91b2313..cd49afe 100644
--- a/src/Truncate/utils.tsx
+++ b/src/Truncate/utils.tsx
@@ -85,13 +85,13 @@ export const getMiddleTruncateFragments = ({
// If current width is less than target width, attempt to expand fragments
if (fullWidth < targetWidth) {
// Try to expand startFragment to utilize available space
- // Only expand if startSliceIndex > 0 (i.e., when end is not greater than text length)
+ // Only expand while there is still uncovered text between the two fragments.
while (
- startSliceIndex > 0 &&
- startSliceIndex + startFragment.length < length &&
+ startFragment.length < length &&
+ startFragment.length + endFragment.length < fullText.length &&
fullWidth < targetWidth
) {
- const nextChar = lastLineText[startSliceIndex + startFragment.length]
+ const nextChar = lastLineText[startFragment.length]
const testStartFragment = startFragment + nextChar
const testWidth = getFragmentsTotalWidth(testStartFragment, endFragment)
@@ -104,7 +104,11 @@ export const getMiddleTruncateFragments = ({
}
// If there's still space available, try to expand endFragment
- while (endFragment.length < fullText.length && fullWidth < targetWidth) {
+ while (
+ endFragment.length < fullText.length &&
+ startFragment.length + endFragment.length < fullText.length &&
+ fullWidth < targetWidth
+ ) {
const nextChar = fullText[fullText.length - endFragment.length - 1]
const testEndFragment = nextChar + endFragment
const testWidth = getFragmentsTotalWidth(startFragment, testEndFragment)
@@ -145,8 +149,6 @@ export const getMiddleTruncateFragments = ({
startFragment = startFragment.slice(0, startFragment.length - 1)
} else if (endFragment.length > 0) {
endFragment = endFragment.slice(1)
- } else {
- break
}
fullWidth = getFragmentsTotalWidth(startFragment, endFragment)
@@ -156,12 +158,18 @@ export const getMiddleTruncateFragments = ({
// This step ensures to use every available pixel efficiently
if (fullWidth < targetWidth) {
const remainingWidth = targetWidth - fullWidth
+ const hasHiddenText =
+ startFragment.length + endFragment.length < fullText.length
// Try to add characters to both ends while maintaining visual balance
// Only add to startFragment if startSliceIndex > 0
const startChar =
- startSliceIndex > 0 ? lastLineText[startFragment.length] : null
- const endChar = fullText[fullText.length - endFragment.length - 1]
+ hasHiddenText && startSliceIndex > 0
+ ? lastLineText[startFragment.length]
+ : null
+ const endChar = hasHiddenText
+ ? fullText[fullText.length - endFragment.length - 1]
+ : null
if (startChar && measureWidth(startChar) <= remainingWidth) {
startFragment += startChar
diff --git a/test/Truncate.spec.tsx b/test/Truncate.spec.tsx
index 0972b59..d3ca716 100644
--- a/test/Truncate.spec.tsx
+++ b/test/Truncate.spec.tsx
@@ -566,6 +566,15 @@ describe('', () => {
describe('getMiddleTruncateFragments', () => {
const targetWidth = width
const ellipsisWidth = measureWidth(ellipsis)
+ const createVariableMeasureWidth = (
+ widths: Record,
+ fallback = 1,
+ ) => {
+ return (text: string) =>
+ text.split('').reduce((total, char) => {
+ return total + (widths[char] ?? fallback)
+ }, 0)
+ }
it('should return correct fragments when text fits within target width', () => {
const options = {
@@ -639,6 +648,154 @@ describe('', () => {
endFragment: 'This is a long text',
})
})
+
+ it('should expand startFragment with the next uncovered character only', () => {
+ const measureCompactWidth = (text: string) => text.length
+ const result = getMiddleTruncateFragments({
+ end: -2,
+ lastLineText: 'abcdef',
+ fullText: 'abcdefghij',
+ targetWidth: 8,
+ ellipsisWidth: 1,
+ measureWidth: measureCompactWidth,
+ })
+
+ expect(result).toEqual({
+ startFragment: 'abcde',
+ endFragment: 'ij',
+ })
+ })
+
+ it('should stop expanding when fragments already cover the full text', () => {
+ const measureCompactWidth = (text: string) => text.length
+ const result = getMiddleTruncateFragments({
+ end: -4,
+ lastLineText: 'abcdef',
+ fullText: 'abcdef',
+ targetWidth: 8,
+ ellipsisWidth: 1,
+ measureWidth: measureCompactWidth,
+ })
+
+ expect(result).toEqual({
+ startFragment: 'ab',
+ endFragment: 'cdef',
+ })
+ })
+
+ it('should break start expansion when the next character exceeds the target width', () => {
+ const result = getMiddleTruncateFragments({
+ end: -1,
+ lastLineText: 'abc',
+ fullText: 'abcdef',
+ targetWidth: 5,
+ ellipsisWidth: 1,
+ measureWidth: createVariableMeasureWidth({ c: 2 }),
+ })
+
+ expect(result).toEqual({
+ startFragment: 'ab',
+ endFragment: 'ef',
+ })
+ })
+
+ it('should expand endFragment when start expansion cannot use the remaining width', () => {
+ const measureCompactWidth = (text: string) => text.length
+ const result = getMiddleTruncateFragments({
+ end: -1,
+ lastLineText: 'abc',
+ fullText: 'abcdef',
+ targetWidth: 6,
+ ellipsisWidth: 1,
+ measureWidth: measureCompactWidth,
+ })
+
+ expect(result).toEqual({
+ startFragment: 'abc',
+ endFragment: 'ef',
+ })
+ })
+
+ it('should break end expansion when prepending one more character exceeds the target width', () => {
+ const result = getMiddleTruncateFragments({
+ end: -1,
+ lastLineText: 'abc',
+ fullText: 'abcdef',
+ targetWidth: 6,
+ ellipsisWidth: 1,
+ measureWidth: createVariableMeasureWidth({ e: 2 }),
+ })
+
+ expect(result).toEqual({
+ startFragment: 'abc',
+ endFragment: 'f',
+ })
+ })
+
+ it('should truncate only the end fragment when the start fragment is empty', () => {
+ const measureCompactWidth = (text: string) => text.length
+ const result = getMiddleTruncateFragments({
+ end: -3,
+ lastLineText: 'abc',
+ fullText: 'abcdef',
+ targetWidth: 2,
+ ellipsisWidth: 1,
+ measureWidth: measureCompactWidth,
+ })
+
+ expect(result).toEqual({
+ startFragment: '',
+ endFragment: 'f',
+ })
+ })
+
+ it('should truncate the wider end fragment first and then trim the start fragment if needed', () => {
+ const result = getMiddleTruncateFragments({
+ end: -1,
+ lastLineText: 'abc',
+ fullText: 'abcdef',
+ targetWidth: 2,
+ ellipsisWidth: 1,
+ measureWidth: createVariableMeasureWidth({ f: 3 }),
+ })
+
+ expect(result).toEqual({
+ startFragment: 'a',
+ endFragment: '',
+ })
+ })
+
+ it('should fine-tune by adding one start character when there is remaining width', () => {
+ const result = getMiddleTruncateFragments({
+ end: -1,
+ lastLineText: 'abc',
+ fullText: 'abcdef',
+ targetWidth: 3,
+ ellipsisWidth: 1,
+ measureWidth: createVariableMeasureWidth({ f: 2 }),
+ })
+
+ expect(result).toEqual({
+ startFragment: 'ab',
+ endFragment: '',
+ })
+ })
+
+ it('should fine-tune by adding one end character when start fine-tuning does not fit', () => {
+ const result = getMiddleTruncateFragments({
+ end: -1,
+ lastLineText: 'abc',
+ fullText: 'abcdef',
+ targetWidth: 4,
+ ellipsisWidth: 1,
+ measureWidth: createVariableMeasureWidth({ b: 2 }),
+ })
+
+ expect(result).toEqual({
+ startFragment: 'a',
+ endFragment: 'ef',
+ })
+ })
})
describe('Testing side effects of other props values', () => {