Skip to content

Commit 5b75dcd

Browse files
authored
feat: Add prompts module with confirm feature (#12)
* test: add new test helpers to test io * feat: add prompts module with first feature "confirm" * test: rename descriptions
1 parent 56abaae commit 5b75dcd

File tree

8 files changed

+662
-1
lines changed

8 files changed

+662
-1
lines changed

Diff for: lib/src/prompts/confirm.dart

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import 'dart:io';
2+
3+
import 'package:cli_tools/cli_tools.dart';
4+
5+
Future<bool> confirm(
6+
String message, {
7+
bool? defaultValue,
8+
required Logger logger,
9+
}) async {
10+
var prompt = defaultValue == null
11+
? '[y/n]'
12+
: defaultValue
13+
? '[Y/n]'
14+
: '[y/N]';
15+
16+
while (true) {
17+
logger.write(
18+
'$message $prompt: ',
19+
LogLevel.info,
20+
newLine: false,
21+
newParagraph: false,
22+
);
23+
var input = stdin.readLineSync()?.trim().toLowerCase();
24+
25+
if (input == null || input.isEmpty) {
26+
if (defaultValue != null) {
27+
return defaultValue;
28+
}
29+
logger.info('Please enter "y" or "n".');
30+
continue;
31+
}
32+
33+
if (input == 'y' || input == 'yes') {
34+
return true;
35+
} else if (input == 'n' || input == 'no') {
36+
return false;
37+
} else {
38+
logger.info('Invalid input. Please enter "y" or "n".');
39+
}
40+
}
41+
}

Diff for: lib/src/prompts/prompts.dart

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export 'confirm.dart';

Diff for: test/better_command_runner/analytics_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'package:args/command_runner.dart';
44
import 'package:cli_tools/better_command_runner.dart';
55
import 'package:test/test.dart';
66

7-
import '../test_utils.dart';
7+
import '../test_utils/test_utils.dart';
88

99
class MockCommand extends Command {
1010
static String commandName = 'mock-command';

Diff for: test/prompts/confirm_test.dart

+287
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
import 'package:cli_tools/cli_tools.dart';
2+
import 'package:cli_tools/src/prompts/confirm.dart';
3+
import 'package:test/test.dart';
4+
5+
import '../test_utils/io_helper.dart';
6+
7+
void main() {
8+
var logger = StdOutLogger(LogLevel.debug);
9+
10+
test(
11+
'Given confirm prompt '
12+
'when providing valid input "yes" '
13+
'then should return true', () async {
14+
late Future<bool> result;
15+
await collectOutput(
16+
stdinLines: ['yes'],
17+
() {
18+
result = confirm(
19+
'Are you sure?',
20+
logger: logger,
21+
);
22+
},
23+
);
24+
25+
await expectLater(
26+
result,
27+
completion(isTrue),
28+
);
29+
});
30+
31+
test(
32+
'Given confirm prompt '
33+
'when providing valid input "y" '
34+
'then should return true', () async {
35+
late Future<bool> result;
36+
await collectOutput(
37+
stdinLines: ['y'],
38+
() {
39+
result = confirm(
40+
'Are you sure?',
41+
logger: logger,
42+
);
43+
},
44+
);
45+
46+
await expectLater(
47+
result,
48+
completion(isTrue),
49+
);
50+
});
51+
52+
test(
53+
'Given confirm prompt '
54+
'when providing valid input capital "Y" '
55+
'then should return true', () async {
56+
late Future<bool> result;
57+
await collectOutput(
58+
stdinLines: ['Y'],
59+
() {
60+
result = confirm(
61+
'Are you sure?',
62+
logger: logger,
63+
);
64+
},
65+
);
66+
67+
await expectLater(
68+
result,
69+
completion(isTrue),
70+
);
71+
});
72+
73+
test(
74+
'Given confirm prompt '
75+
'when providing valid input "no" '
76+
'then should return false', () async {
77+
late Future<bool> result;
78+
await collectOutput(
79+
stdinLines: ['no'],
80+
() async {
81+
result = confirm(
82+
'Are you sure?',
83+
logger: logger,
84+
);
85+
},
86+
);
87+
88+
await expectLater(
89+
result,
90+
completion(isFalse),
91+
);
92+
});
93+
94+
test(
95+
'Given confirm prompt '
96+
'when providing valid input "n" '
97+
'then should return false', () async {
98+
late Future<bool> result;
99+
await collectOutput(
100+
stdinLines: ['n'],
101+
() async {
102+
result = confirm(
103+
'Are you sure?',
104+
logger: logger,
105+
);
106+
},
107+
);
108+
109+
await expectLater(
110+
result,
111+
completion(isFalse),
112+
);
113+
});
114+
115+
test(
116+
'Given confirm prompt '
117+
'when providing valid input capital "N" '
118+
'then should return false', () async {
119+
late Future<bool> result;
120+
await collectOutput(
121+
stdinLines: ['N'],
122+
() async {
123+
result = confirm(
124+
'Are you sure?',
125+
logger: logger,
126+
);
127+
},
128+
);
129+
130+
await expectLater(
131+
result,
132+
completion(isFalse),
133+
);
134+
});
135+
136+
test(
137+
'Given confirm prompt '
138+
'when providing invalid input "invalid" and then valid input "yes" '
139+
'then should prompt again and return true', () async {
140+
late Future<bool> result;
141+
var (:stdout, :stderr, :stdin) = await collectOutput(
142+
stdinLines: ['invalid', 'yes'],
143+
() {
144+
result = confirm(
145+
'Are you sure?',
146+
logger: logger,
147+
);
148+
},
149+
);
150+
151+
expect(
152+
stdout.output,
153+
'Are you sure? [y/n]: '
154+
'Invalid input. Please enter "y" or "n".\n'
155+
'Are you sure? [y/n]: ',
156+
);
157+
158+
await expectLater(
159+
result,
160+
completion(isTrue),
161+
);
162+
});
163+
164+
test(
165+
'Given confirm prompt '
166+
'when providing empty input with default value false '
167+
'then should return false', () async {
168+
late bool result;
169+
await collectOutput(
170+
stdinLines: [' '],
171+
() async {
172+
result = await confirm(
173+
'Are you sure?',
174+
defaultValue: false,
175+
logger: logger,
176+
);
177+
},
178+
);
179+
180+
await expectLater(
181+
result,
182+
isFalse,
183+
);
184+
});
185+
186+
test(
187+
'Given confirm prompt '
188+
'when providing empty input with default value true '
189+
'then should return true', () async {
190+
late Future<bool> result;
191+
await collectOutput(
192+
stdinLines: [' '],
193+
() {
194+
result = confirm(
195+
'Are you sure?',
196+
defaultValue: true,
197+
logger: logger,
198+
);
199+
},
200+
);
201+
202+
await expectLater(
203+
result,
204+
completion(isTrue),
205+
);
206+
});
207+
208+
test(
209+
'Given confirm prompt '
210+
'when providing empty input and then "yes" without default value '
211+
'then should prompt again and return true', () async {
212+
late Future<bool> result;
213+
var (:stdout, :stderr, :stdin) = await collectOutput(
214+
stdinLines: [' ', 'yes'],
215+
() {
216+
result = confirm(
217+
'Are you sure?',
218+
logger: logger,
219+
);
220+
},
221+
);
222+
223+
expect(
224+
stdout.output,
225+
'Are you sure? [y/n]: '
226+
'Please enter "y" or "n".\n'
227+
'Are you sure? [y/n]: ');
228+
229+
await expectLater(
230+
result,
231+
completion(isTrue),
232+
);
233+
});
234+
235+
test(
236+
'Given confirm prompt '
237+
'when providing no default value '
238+
'then should prompt with lowercase "y" and "n"', () async {
239+
var (:stdout, :stderr, :stdin) = await collectOutput(
240+
stdinLines: ['yes'],
241+
() async {
242+
await confirm(
243+
'Are you sure?',
244+
logger: logger,
245+
);
246+
},
247+
);
248+
249+
expect(stdout.output, 'Are you sure? [y/n]: ');
250+
});
251+
252+
test(
253+
'Given confirm prompt '
254+
'when providing default value true '
255+
'then should prompt with uppercase "Y" and lowercase "n"', () async {
256+
var (:stdout, :stderr, :stdin) = await collectOutput(
257+
stdinLines: ['yes'],
258+
() async {
259+
await confirm(
260+
'Are you sure?',
261+
defaultValue: true,
262+
logger: logger,
263+
);
264+
},
265+
);
266+
267+
expect(stdout.output, 'Are you sure? [Y/n]: ');
268+
});
269+
270+
test(
271+
'Given confirm prompt '
272+
'when providing default value false '
273+
'then should prompt with lowercase "y" and uppercase "N"', () async {
274+
var (:stdout, :stderr, :stdin) = await collectOutput(
275+
stdinLines: ['yes'],
276+
() async {
277+
await confirm(
278+
'Are you sure?',
279+
defaultValue: false,
280+
logger: logger,
281+
);
282+
},
283+
);
284+
285+
expect(stdout.output, 'Are you sure? [y/N]: ');
286+
});
287+
}

Diff for: test/test_utils/io_helper.dart

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'dart:async';
2+
import 'dart:io';
3+
4+
import 'mock_stdin.dart';
5+
import 'mock_stdout.dart';
6+
7+
Future<({MockStdout stdout, MockStdout stderr, MockStdin stdin})>
8+
collectOutput<T>(
9+
FutureOr<T> Function() runner, {
10+
List<String> stdinLines = const [],
11+
}) async {
12+
var standardOut = MockStdout();
13+
var standardError = MockStdout();
14+
var standardIn = MockStdin(stdinLines);
15+
16+
await IOOverrides.runZoned(
17+
() async {
18+
return await runner();
19+
},
20+
stdout: () => standardOut,
21+
stderr: () => standardError,
22+
stdin: () => standardIn,
23+
);
24+
25+
return (stdout: standardOut, stderr: standardError, stdin: standardIn);
26+
}

0 commit comments

Comments
 (0)