| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // ignore_for_file: import_of_legacy_library_into_null_safe |
| import 'dart:async'; |
| import 'dart:math'; |
| |
| import 'package:ermine_driver/ermine_driver.dart'; |
| import 'package:ermine_driver/simple_browser_driver.dart'; |
| import 'package:fidl_fuchsia_input/fidl_async.dart'; |
| import 'package:fidl_fuchsia_ui_input3/fidl_async.dart' hide KeyEvent; |
| import 'package:flutter_driver/flutter_driver.dart'; |
| import 'package:gcloud_lib/gcloud_lib.dart'; |
| import 'package:sl4f/sl4f.dart'; |
| import 'package:test/test.dart'; |
| import 'package:webdriver/sync_io.dart'; |
| |
| const _timeoutOneSec = Duration(seconds: 1); |
| const _timeoutThreeSec = Duration(seconds: 3); |
| const _timeoutTenSec = Duration(seconds: 10); |
| const _timeoutPerTest = Timeout(Duration(seconds: 60)); |
| const _sampleViewRect = Rectangle(100, 200, 100, 100); |
| const testserverUrl = |
| 'fuchsia-pkg://fuchsia.com/ermine_testserver#meta/ermine_testserver.cmx'; |
| |
| // Flags to enable/disable each test in order of |
| // 0: Web pages & history navigation test |
| // 1: Video test |
| // 2: Tab control test |
| // 3: Text input field test |
| // 4: Audio test |
| // 5: Keyboard shortcut test. |
| const skipTests = [true, true, false, true, true, true]; |
| |
| void main() { |
| late Sl4f sl4f; |
| late ErmineDriver ermine; |
| late WebDriverConnector webDriverConnector; |
| late Input input; |
| |
| // TODO(fxb/69334): Get rid of the space in the hint text. |
| const newTabHintText = ' SEARCH'; |
| const indexUrl = 'http://127.0.0.1:8080/index.html'; |
| const stopUrl = 'http://127.0.0.1:8080/stop'; |
| final newTabFinder = find.text('NEW TAB'); |
| final indexTabFinder = find.text('Localhost'); |
| final nextTabFinder = find.text('Next Page'); |
| final popupTabFinder = find.text('Popup Page'); |
| final videoTabFinder = find.text('Video Test'); |
| final redTabFinder = find.text('Red Page'); |
| final greenTabFinder = find.text('Green Page'); |
| final blueTabFinder = find.text('Blue Page'); |
| final audioTabFinder = find.text('Audio Test'); |
| |
| setUpAll(() async { |
| sl4f = Sl4f.fromEnvironment(); |
| await sl4f.startServer(); |
| print('Started Sl4f server'); |
| |
| ermine = ErmineDriver(sl4f); |
| await ermine.setUp(); |
| print('Set up Ermine driver'); |
| |
| input = Input(sl4f); |
| print('Set up Input'); |
| |
| webDriverConnector = WebDriverConnector('runtime_deps/chromedriver', sl4f); |
| await webDriverConnector.initialize(); |
| print('Set up and initialized a web driver'); |
| }); |
| |
| tearDownAll(() async { |
| await webDriverConnector.tearDown(); |
| print('Tore down Web driver'); |
| await Future.delayed(Duration(seconds: 1)); |
| await ermine.tearDown(); |
| print('Tore down Ermine flutter driver'); |
| await sl4f.stopServer(); |
| print('Stopped sl4f server'); |
| sl4f.close(); |
| print('Closed sl4f'); |
| }); |
| |
| Future<bool> _waitForTabArrangement(FlutterDriver browser, |
| SerializableFinder leftTabFinder, SerializableFinder rightTabFinder, |
| {Duration timeout = const Duration(seconds: 30)}) async { |
| return ermine.waitFor(() async { |
| final leftTabX = (await browser.getCenter(leftTabFinder)).dx; |
| final rightTabX = (await browser.getCenter(rightTabFinder)).dx; |
| return leftTabX < rightTabX; |
| }, timeout: timeout); |
| } |
| |
| /// Keeps finding a web element that satisfies the given [by] condition until |
| /// the timeout expires. [NoSuchElementException] thrown by [findElement] |
| /// in the meantime is ignored. |
| /// Returns the [WebElement] if it finds one. Otherwise, returns null. |
| Future<WebElement?> _waitForWebElement(WebDriver web, By by) async { |
| return await ermine.waitFor<WebElement?>(() async { |
| try { |
| final element = web.findElement(by); |
| return element; |
| } on NoSuchElementException { |
| return null; |
| } |
| }, timeout: _timeoutTenSec); |
| } |
| |
| /// Keeps calling the given action until it gets the expected result within |
| /// a fixed time. Errors thrown by the action in the meantime is ignored. |
| /// e.g. [DriverError], thrown in case the driver fails to locate a [Finder]. |
| /// Returns true if it gets the expected result. Otherwise, returns false. |
| Future<bool> _repeatActionUntilGetResult( |
| dynamic action(), Future<void> result()) async { |
| return await ermine.waitFor(() async { |
| action.call(); |
| try { |
| await result.call(); |
| return true; |
| // ignore: avoid_catches_without_on_clauses |
| } catch (e) { |
| print('$e. Keep repeating the action until timeout expires...'); |
| return false; |
| } |
| }, timeout: _timeoutTenSec); |
| } |
| |
| /// Keeps calling `waitFor(finder)` until the timeout expires. Returns true |
| /// if it locates one. Otherwise, returns false. |
| Future<bool> _repeatActionWaitingFor( |
| FlutterDriver browser, |
| dynamic action(), |
| SerializableFinder finder, { |
| Duration waitForTimeout = _timeoutOneSec, |
| }) async { |
| return await _repeatActionUntilGetResult( |
| action, () => browser.waitFor(finder, timeout: waitForTimeout)); |
| } |
| |
| /// Keeps calling `waitForAbsent(finder)` until the timeout expires. Returns |
| /// true if it locates one. Otherwise, returns false. |
| Future<bool> _repeatActionWaitingForAbsent( |
| FlutterDriver browser, |
| dynamic action(), |
| SerializableFinder finder, { |
| Duration waitForAbsentTimeout = _timeoutOneSec, |
| }) async { |
| return await _repeatActionUntilGetResult(action, |
| () => browser.waitForAbsent(finder, timeout: waitForAbsentTimeout)); |
| } |
| |
| Future<void> _invokeShortcut(List<Key> keys) async { |
| const pressDuration = 100; |
| final releaseDuration = pressDuration * keys.length + 100; |
| var pressDurations = [ |
| for (var i = 0; i < keys.length; i++) pressDuration + (i * 100) |
| ]; |
| var releaseDurations = [ |
| for (var i = 0; i < keys.length; i++) releaseDuration + (i * 100) |
| ]; |
| |
| await input.keyEvents([ |
| for (var i = 0; i < keys.length; i++) |
| KeyEvent(keys[i], Duration(milliseconds: pressDurations[i]), |
| KeyEventType.pressed), |
| // Releases the key in reverse order. |
| for (var i = 0; i < keys.length; i++) |
| KeyEvent(keys[keys.length - i - 1], |
| Duration(milliseconds: releaseDurations[i]), KeyEventType.released) |
| ]); |
| |
| await ermine.driver |
| .waitUntilNoTransientCallbacks(timeout: Duration(seconds: 2)); |
| } |
| |
| // TODO(fxb/68689): Transition pointer interactions to Sl4f.Input once it is |
| // ready. |
| test('Should be able to do page and history navigation.', () async { |
| // Starts hosting a local http website. |
| // ignore: unawaited_futures |
| ermine.component.launch(testserverUrl); |
| print('Launched the test server .'); |
| |
| FlutterDriver browser; |
| browser = await ermine.launchAndWaitForSimpleBrowser(); |
| |
| // Access to the website. |
| await input.text(indexUrl); |
| print('Typed in $indexUrl to the browser'); |
| await input.keyPress(kEnterKey); |
| print('Pressed Enter'); |
| await browser.waitUntilFirstFrameRasterized(); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.waitFor(indexTabFinder, timeout: _timeoutTenSec); |
| print('Index tab found.'); |
| |
| final webdriver = |
| (await webDriverConnector.webDriversForHost('127.0.0.1')).single; |
| print('Connected a web driver to the localhost'); |
| |
| expect(await browser.getText(indexTabFinder), isNotNull); |
| print('Opened $indexUrl'); |
| |
| final nextLink = await _waitForWebElement(webdriver, By.linkText('Next')); |
| expect(nextLink, isNotNull); |
| |
| // Clicks the text link that opens next.html (page navigation) |
| expect( |
| await _repeatActionWaitingForAbsent( |
| browser, nextLink!.click, indexTabFinder), |
| isTrue, |
| reason: 'Failed to click the Next link.'); |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(nextTabFinder), isNotNull); |
| print('Clicked the next.html link'); |
| |
| final prevLink = await _waitForWebElement(webdriver, By.linkText('Prev')); |
| expect(prevLink, isNotNull); |
| |
| // Clicks the text link that opens index.html (page navigation) |
| expect( |
| await _repeatActionWaitingForAbsent( |
| browser, prevLink!.click, nextTabFinder), |
| isTrue, |
| reason: 'Failed to click the Prev link.'); |
| |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(indexTabFinder), isNotNull); |
| print('Clicked the index.html link'); |
| |
| // Goes back to next.html by tapping the BCK button (history navigation) |
| expect( |
| await _repeatActionWaitingForAbsent(browser, () async { |
| final back = find.byValueKey('back'); |
| await browser.tap(back); |
| }, indexTabFinder), |
| isTrue, |
| reason: 'Failed to hit the BCK button.', |
| ); |
| |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(nextTabFinder), isNotNull); |
| print('Hit BCK'); |
| |
| // Goes forward to index.html by tapping the FWD button (history navigation) |
| expect( |
| await _repeatActionWaitingForAbsent(browser, () async { |
| final forward = find.byValueKey('forward'); |
| await browser.tap(forward); |
| }, nextTabFinder), |
| isTrue, |
| reason: 'Failed to hit the FWD button.', |
| ); |
| |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(indexTabFinder), isNotNull); |
| print('Hit FWD'); |
| |
| // Clicks + button to increase the number |
| var digitLink = await _waitForWebElement(webdriver, By.id('target')); |
| final addButton = await _waitForWebElement(webdriver, By.id('increase')); |
| expect(digitLink!.text, '0'); |
| addButton!.click(); |
| await ermine.waitFor(() async { |
| return digitLink!.text == '1'; |
| }); |
| addButton.click(); |
| await ermine.waitFor(() async { |
| return digitLink!.text == '2'; |
| }); |
| print('Clicked the + button next to the digit three times'); |
| |
| // Refreshes the page |
| final refresh = find.byValueKey('refresh'); |
| await browser.tap(refresh); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| digitLink = await _waitForWebElement(webdriver, By.id('target')); |
| await ermine.waitFor(() async { |
| return digitLink!.text == '0'; |
| }); |
| print('Hit RFRSH'); |
| |
| final popupLink = await _waitForWebElement(webdriver, By.linkText('Popup')); |
| expect(popupLink, isNotNull); |
| |
| // Clicks the text link that opens popup.html (popup page navigation) |
| expect( |
| await _repeatActionWaitingFor(browser, popupLink!.click, popupTabFinder, |
| waitForTimeout: _timeoutThreeSec), |
| isTrue, |
| reason: 'Failed to click the Popup link.'); |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(indexTabFinder), isNotNull); |
| expect(await browser.getText(popupTabFinder), isNotNull); |
| print('Clicked the popup.html link'); |
| |
| // Stops the local http server. |
| await browser.requestData(stopUrl); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.waitFor(find.text(stopUrl), timeout: _timeoutTenSec); |
| |
| // Closes the flutter driver connected to the browser. |
| await browser.close(); |
| |
| // Close the simple browser view. |
| await ermine.threeKeyShortcut(Key.leftCtrl, Key.leftShift, Key.w); |
| await ermine.driver.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await ermine.waitForViewAbsent(simpleBrowserUrl); |
| print('Closed the browser'); |
| }, timeout: _timeoutPerTest, skip: skipTests[0]); |
| |
| test('Should be able to play videos on web pages.', () async { |
| // Starts hosting a local http website. |
| // ignore: unawaited_futures |
| ermine.component.launch(testserverUrl); |
| print('Launched the test server.'); |
| |
| FlutterDriver browser; |
| browser = await ermine.launchAndWaitForSimpleBrowser(); |
| |
| // Access to video.html where the following video is played: |
| // experiences/bin/ermine_testserver/public/simple_browser_test/sample_video.mp4 |
| // It shows the violet-colored background for the first 3 seconds then shows |
| // the fuchsia-colored background for another 3 seconds. |
| await input.text('http://127.0.0.1:8080/video.html'); |
| await input.keyPress(kEnterKey); |
| await browser.waitFor(videoTabFinder, timeout: _timeoutTenSec); |
| |
| expect(await browser.getText(videoTabFinder), isNotNull); |
| print('Opened http://127.0.0.1:8080/video.html'); |
| |
| // Waits for a while for the video to be loaded before taking a screenshot. |
| await Future.delayed(Duration(seconds: 2)); |
| final earlyScreenshot = await ermine.screenshot(_sampleViewRect); |
| |
| // Takes another screenshot after 3 seconds. |
| await Future.delayed(Duration(seconds: 3)); |
| |
| final isVideoPlayed = await ermine.waitFor(() async { |
| final lateScreenshot = await ermine.screenshot(_sampleViewRect); |
| final diff = ermine.screenshotsDiff(earlyScreenshot, lateScreenshot); |
| return diff == 1; |
| }, timeout: _timeoutTenSec); |
| |
| expect(isVideoPlayed, isTrue); |
| print('The video was played'); |
| |
| // Stops the local http server. |
| await browser.requestData(stopUrl); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.waitFor(find.text(stopUrl), timeout: _timeoutTenSec); |
| |
| // Closes the flutter driver connected to the browser. |
| await browser.close(); |
| await ermine.driver.requestData('close'); |
| await ermine.driver.waitForAbsent(find.text('simple-browser.cmx')); |
| expect(await ermine.isStopped(simpleBrowserUrl), isTrue); |
| print('Closed the browser'); |
| }, timeout: _timeoutPerTest, skip: skipTests[1]); |
| |
| test('Should be able to switch, rearrange, and close tabs', () async { |
| // Starts hosting a local http website. |
| expect(await ermine.launch(testserverUrl), isTrue); |
| await ermine.driver.waitUntilNoTransientCallbacks(); |
| print('Launched the test server.'); |
| |
| final browser = SimpleBrowserDriver(ermine); |
| await browser.launchAndWaitForSimpleBrowser(); |
| |
| /// Tab Switching Test |
| const redUrl = 'http://127.0.0.1:8080/red.html'; |
| const greenUrl = 'http://127.0.0.1:8080/green.html'; |
| const blueUrl = 'http://127.0.0.1:8080/blue.html'; |
| |
| // Opens red.html in the second tab leaving the first tab as an empty tab. |
| await input.text(redUrl, keyEventDuration: Duration(milliseconds: 50)); |
| print('Typed in $redUrl to the browser'); |
| await input.keyPress(kEnterKey); |
| print('Pressed Enter'); |
| await browser.driver.waitUntilFirstFrameRasterized(); |
| await browser.driver.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.driver.waitFor(redTabFinder, timeout: _timeoutTenSec); |
| print('Opened red.html'); |
| |
| // Opens green.html in the third tab. |
| await browser.driver.tap(find.byValueKey('new_tab')); |
| await browser.driver |
| .waitFor(find.text(newTabHintText), timeout: _timeoutTenSec); |
| |
| await input.text(greenUrl, keyEventDuration: Duration(milliseconds: 50)); |
| print('Typed in $greenUrl to the browser'); |
| await input.keyPress(kEnterKey); |
| print('Pressed Enter'); |
| await browser.driver.waitUntilFirstFrameRasterized(); |
| await browser.driver.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.driver.waitFor(greenTabFinder, timeout: _timeoutTenSec); |
| print('Opened green.html'); |
| |
| // Opens blue.html in the forth tab. |
| await browser.driver.tap(find.byValueKey('new_tab')); |
| await browser.driver |
| .waitFor(find.text(newTabHintText), timeout: _timeoutTenSec); |
| |
| await input.text(blueUrl, keyEventDuration: Duration(milliseconds: 50)); |
| print('Typed in $blueUrl to the browser'); |
| await input.keyPress(kEnterKey); |
| print('Pressed Enter'); |
| await browser.driver.waitUntilFirstFrameRasterized(); |
| await browser.driver.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.driver.waitFor(blueTabFinder, timeout: _timeoutTenSec); |
| print('Opened blue.html'); |
| |
| // Should have 4 tabs and the forth tab should be focused. |
| expect(await browser.driver.getText(newTabFinder), isNotNull); |
| expect(await browser.driver.getText(redTabFinder), isNotNull); |
| expect(await browser.driver.getText(greenTabFinder), isNotNull); |
| expect(await browser.driver.getText(blueTabFinder), isNotNull); |
| expect(await browser.driver.getText(find.text(blueUrl)), isNotNull); |
| print('The Blue tab is focused'); |
| |
| // The second tab should be focused when tapped. |
| await browser.driver.tap(redTabFinder); |
| await browser.driver.waitFor(find.text(redUrl)); |
| expect(await browser.driver.getText(find.text(redUrl)), isNotNull); |
| print('Clicked the Red tab'); |
| |
| // The thrid tab should be focused when tapped. |
| await browser.driver.tap(greenTabFinder); |
| await browser.driver.waitFor(find.text(greenUrl)); |
| expect(await browser.driver.getText(find.text(greenUrl)), isNotNull); |
| print('Clicked the Green tab'); |
| |
| /// Tab Rearranging Test |
| |
| // Checks the current order of tabs before rearranging tabs. |
| expect( |
| await _waitForTabArrangement( |
| browser.driver, newTabFinder, redTabFinder), |
| isTrue, |
| reason: 'The New tab is not on the left side of the Red tab:'); |
| expect( |
| await _waitForTabArrangement( |
| browser.driver, redTabFinder, greenTabFinder), |
| isTrue, |
| reason: 'The Red tab is not on the left side of the Green tab'); |
| expect( |
| await _waitForTabArrangement( |
| browser.driver, greenTabFinder, blueTabFinder), |
| isTrue, |
| reason: 'The Green tab is not on the left side of the Blue tab'); |
| print('The tabs are in the order of New > Red > Green > Blue'); |
| |
| // Drags the second tab to the right end of the tab list. |
| await browser.driver.scroll(redTabFinder, 600, 0, Duration(seconds: 1)); |
| |
| // The order of tabs after rearranging tabs. |
| expect( |
| await _waitForTabArrangement( |
| browser.driver, newTabFinder, greenTabFinder), |
| isTrue, |
| reason: 'The New tab is not on the left side of the Green tab.'); |
| expect( |
| await _waitForTabArrangement( |
| browser.driver, greenTabFinder, blueTabFinder), |
| isTrue, |
| reason: 'The Green tab is not on the left side of the Blue tab'); |
| expect( |
| await _waitForTabArrangement( |
| browser.driver, blueTabFinder, redTabFinder), |
| isTrue, |
| reason: 'The Blue tab is not on the left side of the Red tab'); |
| print('Moved the Red tab to the right end'); |
| |
| /// Tab closing test |
| final tabCloseFinder = find.byValueKey('tab_close'); |
| await browser.driver.tap(tabCloseFinder); |
| |
| // The red page should be gone and the last tab should be focused. |
| await browser.driver.waitForAbsent(redTabFinder); |
| print('Closed the Red tab'); |
| |
| expect(await browser.driver.getText(newTabFinder), isNotNull); |
| expect(await browser.driver.getText(greenTabFinder), isNotNull); |
| expect(await browser.driver.getText(blueTabFinder), isNotNull); |
| expect(await browser.driver.getText(find.text(blueUrl)), isNotNull); |
| print('The Blue tab is focused'); |
| |
| // TODO(fxb/70265): Test closing an unfocused tab once fxb/68689 is done. |
| |
| await ermine.threeKeyShortcut(Key.leftCtrl, Key.leftShift, Key.w); |
| await ermine.driver.waitUntilNoTransientCallbacks(); |
| await ermine.waitForViewAbsent(simpleBrowserUrl); |
| print('Closed the browser'); |
| |
| await ermine.threeKeyShortcut(Key.leftCtrl, Key.leftShift, Key.w); |
| await ermine.driver.waitUntilNoTransientCallbacks(); |
| await ermine.waitForViewAbsent(testserverUrl); |
| print('Closed the test server'); |
| |
| // Closes the flutter driver connected to the browser. |
| await browser.tearDown(); |
| print('Tore down the browser driver'); |
| }, timeout: _timeoutPerTest, skip: skipTests[2]); |
| |
| test('Should be able enter text into web text fields', () async { |
| // Starts hosting a local http website. |
| expect(await ermine.launch(testserverUrl), isTrue); |
| await ermine.driver.waitUntilNoTransientCallbacks(); |
| print('Launched the test server.'); |
| |
| FlutterDriver browser; |
| browser = await ermine.launchAndWaitForSimpleBrowser(); |
| |
| const testInputPage = 'http://127.0.0.1:8080/input.html'; |
| final textInputTabFinder = find.text('Text Input'); |
| |
| // Access to the website. |
| await input.text(testInputPage); |
| await input.keyPress(kEnterKey); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.waitFor(textInputTabFinder, timeout: _timeoutTenSec); |
| print('Opened $testInputPage'); |
| |
| final webdriver = |
| (await webDriverConnector.webDriversForHost('127.0.0.1')).single; |
| |
| final textField = await _waitForWebElement(webdriver, By.id('text-input')); |
| print('The textfield is found.'); |
| |
| expect(textField, isNotNull); |
| |
| textField!.click(); |
| await ermine.waitFor(() async { |
| return webdriver.activeElement!.equals(textField); |
| }, timeout: _timeoutTenSec); |
| |
| print('The textfield is now focused.'); |
| |
| // TODO(fxb/74070): Sl4f.Input currently does not work for web elements. |
| // Replace the following line with Sl4f.Input once that is fixed. |
| const testText = 'hello fuchsia'; |
| textField.sendKeys(testText); |
| await ermine.waitFor(() async { |
| return textField.properties['value'] == testText; |
| }, timeout: _timeoutTenSec); |
| print('Text is entered into the textfield.'); |
| |
| // Closes the flutter driver connected to the browser. |
| await browser.close(); |
| |
| await ermine.driver.requestData('closeAll'); |
| print('Closed all views'); |
| }, timeout: _timeoutPerTest, skip: skipTests[3]); |
| |
| test('Should be able to play audios on web', () async { |
| // Starts hosting a local http website. |
| // ignore: unawaited_futures |
| ermine.component.launch(testserverUrl); |
| print('Launched the test server.'); |
| |
| FlutterDriver browser; |
| browser = await ermine.launchAndWaitForSimpleBrowser(); |
| |
| final record = Audio(sl4f); |
| final gcloud = GCloud(); |
| |
| // Access to audio.html where the following audio is played: |
| // experiences/bin/ermine_testserver/public/simple_browser_test/sample_audio.mp3 |
| // It plays human voice saying "How old is Obama". |
| await input.text('http://127.0.0.1:8080/audio.html'); |
| await input.keyPress(kEnterKey); |
| await browser.waitFor(audioTabFinder, timeout: _timeoutTenSec); |
| |
| expect(await browser.getText(audioTabFinder), isNotNull); |
| print('Opened http://127.0.0.1:8080/audio.html'); |
| |
| final webdriver = |
| (await webDriverConnector.webDriversForHost('127.0.0.1')).single; |
| |
| final audio = await _waitForWebElement(webdriver, By.id('audio')); |
| expect(audio, isNotNull); |
| print('The textfield is found.'); |
| |
| final playButton = await _waitForWebElement(webdriver, By.id('play')); |
| expect(playButton, isNotNull); |
| print('The PLAY button is found.'); |
| |
| // Note that it doesn't work locally. You should create GCloud using |
| // `GCloud.withClientViaApiKey()` with an API key for local testing. |
| await gcloud.setClientFromMetadata(); |
| print('Set an authenticated gcloud client'); |
| |
| // Plays the audio, records it, sends it to gcloud for speech-to-text, and |
| // verifies if the text result is what we expect. |
| // Retries this process for a few more times if it fails since the audio |
| // sometimes sounds janky. |
| final ttsResult = await ermine.waitFor(() async { |
| print('Start recording audio.'); |
| await record.startOutputSave(); |
| await Future.delayed(_timeoutOneSec); |
| playButton!.click(); |
| |
| // Waits for the audio being played to the end. |
| await Future.delayed(Duration(seconds: 5)); |
| |
| await record.stopOutputSave(); |
| final audioOutput = await record.getOutputAudio(); |
| print('Stopped recording audio.'); |
| |
| final ttsList = |
| await speechToText(gcloud.speech, audioOutput.audioData, 'en-us'); |
| final tts = ttsList.first.toLowerCase(); |
| print('STT result: $tts'); |
| return tts == 'how old is obama'; |
| }); |
| |
| expect(ttsResult, isTrue); |
| |
| gcloud.close(); |
| |
| // Stops the local http server. |
| await browser.requestData(stopUrl); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.waitFor(find.text(stopUrl), timeout: _timeoutTenSec); |
| |
| // Closes the flutter driver connected to the browser. |
| await browser.close(); |
| await ermine.driver.requestData('close'); |
| await ermine.driver.waitForAbsent(find.text('simple-browser.cmx')); |
| expect(await ermine.isStopped(simpleBrowserUrl), isTrue); |
| print('Closed the browser'); |
| }, timeout: _timeoutPerTest, skip: skipTests[4]); |
| |
| test('Should be able to control the browser with keyboard shortcuts', |
| () async { |
| // Starts hosting a local http website. |
| // ignore: unawaited_futures |
| ermine.component.launch(testserverUrl); |
| print('Launched the test server.'); |
| |
| FlutterDriver browser; |
| browser = await ermine.launchAndWaitForSimpleBrowser(); |
| |
| // Opens index.html |
| await input.text(indexUrl, keyEventDuration: Duration(milliseconds: 10)); |
| await input.keyPress(kEnterKey); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.waitFor(indexTabFinder, timeout: _timeoutTenSec); |
| |
| final webdriver = |
| (await webDriverConnector.webDriversForHost('127.0.0.1')).single; |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(indexTabFinder), isNotNull); |
| print('Opened $indexUrl'); |
| |
| // Clicks the + buttons |
| var digitLink = await _waitForWebElement(webdriver, By.id('target')); |
| final addButton = await _waitForWebElement(webdriver, By.id('increase')); |
| expect(digitLink!.text, '0'); |
| addButton!.click(); |
| await ermine.waitFor(() async { |
| return digitLink!.text == '1'; |
| }); |
| addButton.click(); |
| await ermine.waitFor(() async { |
| return digitLink!.text == '2'; |
| }); |
| print('Clicked the + button next to the digit three times'); |
| |
| // Shortcut for refresh (Ctrl + r) |
| await _invokeShortcut([Key.leftCtrl, Key.r]); |
| digitLink = await _waitForWebElement(webdriver, By.id('target')); |
| await ermine.waitFor(() async { |
| return digitLink!.text == '0'; |
| }); |
| print('Refreshed the page'); |
| |
| // Clicks the 'Next' link |
| final nextLink = await _waitForWebElement(webdriver, By.linkText('Next')); |
| expect(nextLink, isNotNull); |
| expect( |
| await _repeatActionWaitingForAbsent( |
| browser, nextLink!.click, indexTabFinder), |
| isTrue, |
| reason: 'Failed to click the Next link.'); |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(nextTabFinder), isNotNull); |
| print('Clicked the next.html link'); |
| |
| // Shortcut for backward (Alt + ←) |
| expect( |
| await _repeatActionWaitingForAbsent( |
| browser, |
| () async => await _invokeShortcut([Key.leftAlt, Key.left]), |
| nextTabFinder), |
| isTrue, |
| reason: 'Failed to invoke the shortcut for navigating back.'); |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(indexTabFinder), isNotNull); |
| print('Navigated back to index.html'); |
| |
| // Shortcut for forward (Alt + →) |
| expect( |
| await _repeatActionWaitingForAbsent( |
| browser, |
| () async => await _invokeShortcut([Key.leftAlt, Key.right]), |
| nextTabFinder), |
| isTrue, |
| reason: 'Failed to invoke the shortcut for navigating forward.'); |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(indexTabFinder), isNotNull); |
| print('Navigated forward to next.html'); |
| |
| // Shortcut for opening a new tab (Ctrl + t) |
| await _invokeShortcut([Key.leftCtrl, Key.t]); |
| await browser.waitFor(find.text(newTabHintText), timeout: _timeoutTenSec); |
| |
| // Opens blue.html |
| const blueUrl = 'http://127.0.0.1:8080/blue.html'; |
| const nextUrl = 'http://127.0.0.1:8080/next.html'; |
| await input.text(blueUrl, keyEventDuration: Duration(milliseconds: 10)); |
| await input.keyPress(kEnterKey); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.waitFor(blueTabFinder, timeout: _timeoutTenSec); |
| print('Opened blue.html'); |
| |
| // Shortcut for selecting the next tab (Ctrl + Tab) x 2 |
| await _invokeShortcut([Key.leftCtrl, Key.tab]); |
| await browser.waitFor(find.text(newTabHintText)); |
| expect(await browser.getText(find.text(newTabHintText)), isNotNull); |
| print('The new tab is now selected'); |
| |
| await _invokeShortcut([Key.leftCtrl, Key.tab]); |
| await browser.waitFor(find.text(nextUrl)); |
| expect(await browser.getText(find.text(nextUrl)), isNotNull); |
| print('The next tab is now selected'); |
| |
| // Shortcut for selecting the previous tab (Ctrl + Shift +Tab) x 2 |
| await _invokeShortcut([Key.leftCtrl, Key.leftShift, Key.tab]); |
| await browser.waitFor(find.text(newTabHintText)); |
| expect(await browser.getText(find.text(newTabHintText)), isNotNull); |
| print('The new tab is now selected'); |
| |
| await _invokeShortcut([Key.leftCtrl, Key.leftShift, Key.tab]); |
| await browser.waitFor(find.text(blueUrl)); |
| expect(await browser.getText(find.text(blueUrl)), isNotNull); |
| print('The blue tab is now selected'); |
| |
| // Shortcut for closing a current tab (Ctrl + w) |
| await _invokeShortcut([Key.leftCtrl, Key.w]); |
| await browser.waitForAbsent(blueTabFinder); |
| print('Closed the blue tab'); |
| |
| expect(await browser.getText(newTabFinder), isNotNull); |
| expect(await browser.getText(find.text(nextUrl)), isNotNull); |
| print('The index tab is focused'); |
| |
| // Stops the local http server. |
| await browser.requestData(stopUrl); |
| await browser.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await browser.waitFor(find.text(stopUrl), timeout: _timeoutTenSec); |
| |
| // Closes the flutter driver connected to the browser. |
| await browser.close(); |
| await ermine.threeKeyShortcut(Key.leftCtrl, Key.leftShift, Key.w); |
| await ermine.driver.waitUntilNoTransientCallbacks(timeout: _timeoutTenSec); |
| await ermine.waitForViewAbsent(simpleBrowserUrl); |
| print('Closed the browser'); |
| }, timeout: _timeoutPerTest, skip: skipTests[5]); |
| } |