diff --git a/tap-snapshots/test/lib/docs.js.test.cjs b/tap-snapshots/test/lib/docs.js.test.cjs index cbbf8cda7b448..453089abfafe4 100644 --- a/tap-snapshots/test/lib/docs.js.test.cjs +++ b/tap-snapshots/test/lib/docs.js.test.cjs @@ -1290,7 +1290,8 @@ a semver. Like the \`rc\` in \`1.2.0-rc.8\`. #### \`progress\` -* Default: \`true\` unless running in a known CI system +* Default: \`true\` when not in CI and both stderr and stdout are TTYs and not + in a dumb terminal * Type: Boolean When set to \`true\`, npm will display a progress bar during time intensive diff --git a/workspaces/config/lib/definitions/definitions.js b/workspaces/config/lib/definitions/definitions.js index bb24f33afedad..74c2a7b3a8a95 100644 --- a/workspaces/config/lib/definitions/definitions.js +++ b/workspaces/config/lib/definitions/definitions.js @@ -1571,9 +1571,9 @@ const definitions = { }, }), progress: new Definition('progress', { - default: !ciInfo.isCI, + default: !(ciInfo.isCI || !process.stderr.isTTY || !process.stdout.isTTY || process.env.TERM === 'dumb'), defaultDescription: ` - \`true\` unless running in a known CI system + \`true\` when not in CI and both stderr and stdout are TTYs and not in a dumb terminal `, type: Boolean, description: ` @@ -1583,11 +1583,8 @@ const definitions = { Set to \`false\` to suppress the progress bar. `, flatten (key, obj, flatOptions) { - flatOptions.progress = !obj.progress ? false - // progress is only written to stderr but we disable it unless stdout is a tty - // also. This prevents the progress from appearing when piping output to another - // command which doesn't break anything, but does look very odd to users. - : !!process.stderr.isTTY && !!process.stdout.isTTY && process.env.TERM !== 'dumb' + // Only show progress if explicitly enabled AND we have proper TTY environment + flatOptions.progress = !!obj.progress && !!process.stderr.isTTY && !!process.stdout.isTTY && process.env.TERM !== 'dumb' }, }), provenance: new Definition('provenance', { diff --git a/workspaces/config/test/definitions/definitions.js b/workspaces/config/test/definitions/definitions.js index 2090e0dd32008..6a75f0d84e17b 100644 --- a/workspaces/config/test/definitions/definitions.js +++ b/workspaces/config/test/definitions/definitions.js @@ -402,6 +402,7 @@ t.test('progress', t => { const flat = {} + // Test flatten function behavior mockDefs().progress.flatten('progress', {}, flat) t.strictSame(flat, { progress: false }) @@ -417,6 +418,59 @@ t.test('progress', t => { mockDefs().progress.flatten('progress', { progress: true }, flat) t.strictSame(flat, { progress: false }) + // Ensures consistency between default and flatOptions behavior + t.test('default value consistency', t => { + // Test case 1: Both TTYs, normal terminal, not CI + setEnv({ tty: true, term: 'xterm' }) + const def1 = mockDefs({ 'ci-info': { isCI: false, name: null } }).progress + t.equal(def1.default, true, 'default should be true when both TTYs, normal terminal, and not CI') + + // Test case 2: No TTYs, not CI + setEnv({ tty: false, term: 'xterm' }) + const def2 = mockDefs({ 'ci-info': { isCI: false, name: null } }).progress + t.equal(def2.default, false, 'default should be false when no TTYs') + + // Test case 3: Both TTYs but dumb terminal, not CI + setEnv({ tty: true, term: 'dumb' }) + const def3 = mockDefs({ 'ci-info': { isCI: false, name: null } }).progress + t.equal(def3.default, false, 'default should be false in dumb terminal') + + // Test case 4: Mixed TTY states, not CI + mockGlobals(t, { + 'process.stderr.isTTY': true, + 'process.stdout.isTTY': false, + 'process.env.TERM': 'xterm', + }) + const def4 = mockDefs({ 'ci-info': { isCI: false, name: null } }).progress + t.equal(def4.default, false, 'default should be false when only one TTY') + + // Test case 5: Good TTY environment but in CI + setEnv({ tty: true, term: 'xterm' }) + const def5 = mockDefs({ 'ci-info': { isCI: true, name: 'github-actions' } }).progress + t.equal(def5.default, false, 'default should be false in CI even with good TTY environment') + + t.end() + }) + + // Test that flatten behavior is independent of CI detection + t.test('flatten function ignores CI detection', t => { + const flatObj = {} + + // Test that CI doesn't affect flatten behavior when user explicitly enables + setEnv({ tty: true, term: 'xterm' }) + const defsCI = mockDefs({ 'ci-info': { isCI: true, name: 'github-actions' } }) + defsCI.progress.flatten('progress', { progress: true }, flatObj) + t.equal(flatObj.progress, true, 'flatten should enable progress in CI if user explicitly sets true and TTY is available') + + // Test that non-CI doesn't guarantee flatten success if TTY is bad + setEnv({ tty: false, term: 'xterm' }) + const defsNoCI = mockDefs({ 'ci-info': { isCI: false, name: null } }) + defsNoCI.progress.flatten('progress', { progress: true }, flatObj) + t.equal(flatObj.progress, false, 'flatten should disable progress outside CI if TTY is not available') + + t.end() + }) + t.end() })