import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; /// Dictating into a text (Halkyon Meditate's ADR 0038, D15): the words /// heard go where the cursor stands, set apart by a space, and listening /// starts again after the recogniser's pause while the person leaves it /// on, until it hears nothing a few times running. void main() { group('the words heard go', () { TextEditingValue at(String text, int offset) => TextEditingValue( text: text, selection: TextSelection.collapsed(offset: offset), ); test('after the text, a space between, the cursor after them', () { final value = DictationInsertion.at(at('I was', 5)).withWords('at sea'); expect(value.text, 'I was at sea'); expect(value.selection, const TextSelection.collapsed(offset: 12)); }); test('where the cursor stands, set apart on both sides', () { final value = DictationInsertion.at( at('I was there.', 5), ).withWords('at sea'); expect(value.text, 'I was at sea there.'); expect(value.selection, const TextSelection.collapsed(offset: 12)); }); test('with no space added beside one there already, nor at the ' "text's ends", () { expect( DictationInsertion.at(at('', 0)).withWords('A house').text, 'A house', ); expect( DictationInsertion.at(at('I was ', 6)).withWords('home').text, 'I was home', ); expect( DictationInsertion.at(at(' there', 0)).withWords('Out').text, 'Out there', ); }); test('in place of what is selected, as typing does', () { final value = DictationInsertion.at( const TextEditingValue( text: 'a red house', selection: TextSelection(baseOffset: 2, extentOffset: 5), ), ).withWords('blue'); expect(value.text, 'a blue house'); }); test('at the end where the field has no cursor yet', () { expect( DictationInsertion.at(const TextEditingValue(text: 'I was')) .withWords('home') .text, 'I was home', ); }); test('nothing heard leaves the text as it was', () { expect(DictationInsertion.at(at('I was', 5)).withWords('').text, 'I was'); }); }); group('a dictation', () { late FakeSpeechInput speech; late TextEditingController text; late Dictation dictation; setUp(() { speech = FakeSpeechInput(); text = TextEditingController(); dictation = Dictation(speech, text); }); tearDown(() { dictation.dispose(); text.dispose(); }); test('listens in the language asked, writing the words as they come, ' 'and shows them', () async { expect(await dictation.start(language: 'de'), SpeechStart.listening); expect(dictation.listening, isTrue); expect(speech.languages, ['de']); speech.say('Ich war'); expect(dictation.heard, 'Ich war'); speech.say('Ich war am Meer'); expect(text.text, 'Ich war am Meer'); }); test('stopped, it listens no more and keeps what it wrote', () async { await dictation.start(language: 'en'); speech.say('A house'); await dictation.stop(); expect(dictation.listening, isFalse); expect(speech.listening, isFalse); expect(text.text, 'A house'); speech.say('ignored'); expect(text.text, 'A house'); }); test("after the recogniser's pause it listens again, the next words " 'after the last', () async { await dictation.start(language: 'en'); speech ..say('A house') ..finish(); await pumpEventQueue(); expect(dictation.listening, isTrue); expect(speech.listening, isTrue); expect(speech.languages, ['en', 'en']); speech.say('by the sea'); expect(text.text, 'A house by the sea'); }); test( 'it stops by itself after hearing nothing three times running', () async { await dictation.start(language: 'en'); for (var pause = 0; pause < Dictation.silencesBeforeStopping; pause++) { expect(dictation.listening, isTrue, reason: 'pause $pause'); speech.finish(); await pumpEventQueue(); } expect(dictation.listening, isFalse); expect(speech.listening, isFalse); }, ); test('words heard count the silences again from none', () async { await dictation.start(language: 'en'); speech.finish(); await pumpEventQueue(); speech ..say('A house') ..finish(); await pumpEventQueue(); speech.finish(); await pumpEventQueue(); expect(dictation.listening, isTrue); }); test('where the device cannot listen, it says so and stays off', () async { speech.listens = false; expect(await dictation.start(language: 'en'), SpeechStart.unavailable); expect(dictation.listening, isFalse); }); test('where no recogniser is installed, it says so and stays off, ' 'never stuck listening', () async { speech.hasRecognizer = false; var told = 0; dictation.addListener(() => told++); expect(await dictation.start(language: 'en'), SpeechStart.noRecognizer); expect(dictation.listening, isFalse); expect(told, 2, reason: 'on, then off again'); }); group('through a voice input app (ADR 0038, D18)', () { setUp(() => speech.app = true); test('writes what it returns where the cursor stood, once, and ' 'listens no more', () async { text.value = const TextEditingValue( text: 'I was there.', selection: TextSelection.collapsed(offset: 5), ); final started = dictation.start(language: 'en', prompt: 'Your dream'); await pumpEventQueue(); expect(dictation.listening, isTrue, reason: 'while the app is open'); expect(speech.appOpen, isTrue); expect(speech.listening, isFalse, reason: 'nothing else listens'); expect(speech.prompts, ['Your dream']); speech.answer('at sea'); expect(await started, SpeechStart.finished); expect(text.text, 'I was at sea there.'); expect(text.selection, const TextSelection.collapsed(offset: 12)); expect(dictation.listening, isFalse); expect(speech.languages, ['en'], reason: 'not begun again'); }); test('cancelled, writes nothing', () async { text.text = 'I was'; final started = dictation.start(language: 'en'); await pumpEventQueue(); speech.cancel(); expect(await started, SpeechStart.finished); expect(text.text, 'I was'); expect(dictation.listening, isFalse); }); test('stopped while the app is open, writes nothing it returns ' 'later', () async { final started = dictation.start(language: 'en'); await pumpEventQueue(); await dictation.stop(); expect(dictation.listening, isFalse); speech.answer('late words'); await started; expect(text.text, isEmpty); }); }); test('tells its listeners as it starts, hears and stops', () async { var told = 0; dictation.addListener(() => told++); await dictation.start(language: 'en'); speech.say('A'); await dictation.stop(); expect(told, greaterThanOrEqualTo(3)); }); }); }