Skip to content

Commit 7c213f2

Browse files
authored
fix: avoid failure when terminal has no columns (#869)
1 parent 34aed53 commit 7c213f2

File tree

3 files changed

+80
-2
lines changed

3 files changed

+80
-2
lines changed

lib/src/logger_extension.dart

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1+
import 'dart:io';
12
import 'package:mason_logger/mason_logger.dart';
3+
import 'package:meta/meta.dart';
24
import 'package:universal_io/io.dart';
35

6+
/// The fallback number of columns of the terminal.
7+
///
8+
/// Used when the terminal number of columns (width) cannot be determined. For
9+
/// example, when running in Azure DevOps, see relevant issue:
10+
/// https://github.com/VeryGoodOpenSource/very_good_cli/issues/866 .
11+
@visibleForTesting
12+
const fallbackStdoutTerminalColumns = 80;
13+
414
/// Extension on the Logger class for custom styled logging.
515
extension LoggerX on Logger {
616
/// Log a message in the "created" style of the CLI.
@@ -11,13 +21,22 @@ extension LoggerX on Logger {
1121
/// Wrap the [text] to fit perfectly within the width of the terminal when
1222
/// [print]ed.
1323
///
14-
/// To overwrite the width you can use [length].
24+
/// The text will wrap around the terminal width, and will not break words. If
25+
/// the terminal width cannot be determined, the text will wrap around the
26+
/// [fallbackStdoutTerminalColumns].
27+
///
28+
/// To completely overwrite the width you can use [length].
1529
void wrap(
1630
String? text, {
1731
required void Function(String?) print,
1832
int? length,
1933
}) {
20-
final maxLength = length ?? stdout.terminalColumns;
34+
final maxLength = switch (length) {
35+
== null when stdout.hasTerminal => stdout.terminalColumns,
36+
== null when !stdout.hasTerminal => fallbackStdoutTerminalColumns,
37+
_ => length!
38+
};
39+
2140
for (final sentence in text?.split('/n') ?? <String>[]) {
2241
final words = sentence.split(' ');
2342

test/src/command_runner_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ void main() {
199199
when(() => versionFile.readAsStringSync()).thenReturn('0.0.0');
200200

201201
stdout = _MockStdout();
202+
when(() => stdout.hasTerminal).thenReturn(true);
202203
when(() => stdout.supportsAnsiEscapes).thenReturn(true);
203204
when(() => stdout.terminalColumns).thenReturn(30);
204205
});

test/src/logger_extension_test.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
import 'package:mason/mason.dart';
22
import 'package:mocktail/mocktail.dart';
33
import 'package:test/test.dart';
4+
import 'package:universal_io/io.dart';
45
import 'package:very_good_cli/src/logger_extension.dart';
56

67
class _MockLogger extends Mock implements Logger {}
78

9+
class _MockStdout extends Mock implements Stdout {}
10+
811
void main() {
912
group('LoggerX', () {
1013
late Logger logger;
14+
late Stdout stdout;
1115

1216
setUp(() {
1317
logger = _MockLogger();
18+
stdout = _MockStdout();
19+
20+
when(() => stdout.hasTerminal).thenReturn(true);
1421
});
1522

1623
test('created', () {
@@ -45,6 +52,57 @@ void main() {
4552
() => logger.info(any(that: equals('1 2 3 4 5 '))),
4653
]);
4754
});
55+
56+
test(
57+
'''defaults to `fallbackStdoutTerminalColumns` when there is no terminal''',
58+
() async {
59+
await IOOverrides.runZoned(
60+
stdout: () => stdout,
61+
() async {
62+
when(() => stdout.hasTerminal).thenReturn(false);
63+
const stdoutException = StdoutException('');
64+
when(() => stdout.terminalColumns).thenThrow(stdoutException);
65+
66+
final longWord = Iterable.generate(
67+
fallbackStdoutTerminalColumns,
68+
(_) => '1',
69+
).join();
70+
const shortWord = '1';
71+
72+
logger.wrap('$longWord $shortWord', print: logger.info);
73+
74+
verifyInOrder([
75+
() => logger.info(
76+
any(
77+
that: equals('$longWord '),
78+
),
79+
),
80+
() => logger.info(
81+
any(that: equals('$shortWord ')),
82+
),
83+
]);
84+
},
85+
);
86+
},
87+
);
88+
89+
test(
90+
'''throws when an unknown exception occurs when reading `stdout.terminalColumns`''',
91+
() async {
92+
await IOOverrides.runZoned(
93+
stdout: () => stdout,
94+
() async {
95+
final unknownException = Exception();
96+
when(() => stdout.terminalColumns).thenThrow(unknownException);
97+
98+
expect(
99+
() => logger.wrap('test', print: logger.info),
100+
throwsA(equals(unknownException)),
101+
);
102+
},
103+
);
104+
},
105+
);
48106
});
49107
});
50108
}

0 commit comments

Comments
 (0)