├── CHANGELOG.md ├── LICENSE ├── README.md ├── RobotLibrary ├── Library │ ├── RobotLibrary.py │ ├── __init__.py │ ├── variables.py │ └── webDriver.py ├── Results │ └── log.html ├── Tests │ ├── Basic_tests.robot │ ├── test_3_Grid.robot │ ├── test_4_DetailsScreen.robot │ ├── test_6_Encards.robot │ ├── test_7_EpisodePickerScreen.robot │ ├── test_7_EpisodePicker_focus.robot │ ├── test_7_EpisodePicker_utils.robot │ ├── test_9_Bookmarks.robot │ ├── test_ParagraphView.robot │ ├── test_Roku_Recommends.robot │ ├── test_SearchView.robot │ ├── test_TimeGridView.robot │ ├── test_audio_mode.robot │ ├── test_deepLinking.robot │ ├── test_input.robot │ ├── test_timer.robot │ └── test_video_preloading.robot ├── channel.zip ├── multipleDevices │ ├── Basic_tests_multi_device.robot │ ├── config.json │ ├── multi.py │ ├── template.html │ └── test_3_Grid_multi_device.robot ├── requirements.txt ├── setup.py ├── tutorialTests │ ├── step_1.robot │ ├── step_10.robot │ ├── step_11.robot │ ├── step_12.robot │ ├── step_13.robot │ ├── step_2.robot │ ├── step_3.robot │ ├── step_4.robot │ ├── step_5.robot │ ├── step_6.robot │ ├── step_7.robot │ ├── step_8.robot │ └── step_9.robot └── uTest │ ├── _init_.py │ ├── response_mock.py │ ├── test_RobotLibrary.py │ └── test_webDriver.py ├── bin ├── RokuWebDriver_linux ├── RokuWebDriver_mac └── RokuWebDriver_win.exe ├── channels ├── 3_Grid.zip ├── 4_DetailsScreen.zip ├── 6_Endcards.zip ├── 7_EpisodePickerScreen.zip ├── 9_Bookmarks.zip ├── ParagraphView.zip ├── Roku_Recommends.zip ├── SearchView.zip ├── TimeGridView.zip ├── audio_mode.zip └── video_preloading.zip ├── jsLibrary ├── library │ ├── client.js │ └── rokuLibrary.js ├── multipleDevices │ ├── config.json │ ├── multi.js │ ├── multiple_devices_test_3-Grid.js │ ├── multiple_devices_test_basics.js │ ├── template.html │ └── utils.js ├── package.json ├── tests │ ├── test_3-Grid.js │ ├── test_3-Grid_utils.js │ ├── test_4-DetailsScreen.js │ ├── test_6-Encards.js │ ├── test_7-EpisodePicker.js │ ├── test_7_EpisodePicker_focus.js │ ├── test_9-Bookmarks.js │ ├── test_ParagraphView.js │ ├── test_Roku-Recommends.js │ ├── test_Roku-Recommends_input.js │ ├── test_Roku_Recommends_deepLinking.js │ ├── test_SearchView.js │ ├── test_TimeGridView.js │ ├── test_audio-mode.js │ ├── test_basic.js │ └── test_video-preloading.js └── uTest │ ├── responses.js │ ├── test_client.js │ └── test_rokuLibrary.js ├── sample ├── Postman │ ├── README.txt │ └── WebDriver_endpoints ├── channel.zip └── script │ ├── main.py │ ├── multipleDeviceSample.py │ └── webDriver.py └── src ├── ecpClient ├── ecp_client.go ├── ecp_client_test.go ├── ecp_parser.go ├── http_client.go ├── http_client_mock.go ├── response_mocks.go └── response_structure.go ├── httpServer ├── handlers.go ├── handlers_test.go ├── middleware.go ├── response_mock.go ├── response_structure.go ├── routing.go ├── server.go └── utils.go ├── main.go └── version └── version.go /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # **Roku Automated Channel Testing** 2 | 3 | ## v.2.3.0 (September 2021) 4 | 5 | ### Features 6 | 7 | * Added option to run the Roku WebDriver on a specific port. 8 | 9 | ### Bug Fixes 10 | 11 | * Fixed password validation for "/load" endpoint. 12 | * Updated Roku Recommends sample channel. 13 | * Adjusted sample tests that leverage Roku Recommends and SearchView channels. 14 | 15 | ## v.2.2.0 (April 2021) 16 | 17 | ### Features 18 | 19 | * Added pre-built WebDrivers for iOS, Linux, and Windows. 20 | * Added option for installing Python version of Robot library as a local Python package. 21 | 22 | ### Bug Fixes 23 | 24 | * Fixed submitting '@' symbol over "Send word" keyword. 25 | * Fixed "Get player info" keyword failing when called before playback starts 26 | * Adjusted sample tests for Robot and JS libraries (added **Sideload** command to automate sideloading of sample channels used for Robot and JavaScript sample tests). 27 | 28 | ## v.2.1.0 (July 2020) 29 | 30 | ### Features 31 | 32 | * Added "Get child nodes" method for Robot and JavaScript libraries. 33 | * Updated WebDriver **/element/active** endpoint to consistently return correct element. 34 | * Updated WebDriver **/elements** endpoint to returns correct elements when multiple locators are specified. 35 | * Added and updated sample tests for Robot and JavaScript libraries. 36 | 37 | ## v.2.0.0 (March 2020) 38 | 39 | ### Features 40 | 41 | * JavaScript client. 42 | * Channel side loading. 43 | * Input deep linking. 44 | * Timers. 45 | * Multiple device support for Robot and JavaScript libraries. 46 | 47 | ## v.1.0.0 (December 2019) 48 | 49 | ### Features 50 | 51 | * Initial Release 52 | * Initial version of a Roku WebDriver. 53 | * Key press simulation. 54 | * Grab UI elements. 55 | * Current app metadata. 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Roku Automated Channel Testing 2 | Copyright © 2019 Roku, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /RobotLibrary/Library/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/RobotLibrary/Library/__init__.py -------------------------------------------------------------------------------- /RobotLibrary/Library/variables.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | def get_variables(): 18 | variables = { 19 | 'ip_address': '192.168.1.23', 20 | 'server_path': 'D:/projects/go/webDriver/src/main.exe', 21 | 'timeout': 20000, 22 | 'pressDelay': 2000 23 | } 24 | return variables -------------------------------------------------------------------------------- /RobotLibrary/Library/webDriver.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | import requests 18 | import json 19 | from time import sleep 20 | 21 | class WebDriver: 22 | def __init__(self, roku_ip_address: str, timeout: int, pressDelay: int): 23 | data = {'ip' : roku_ip_address, 'timeout': timeout, 'pressDelay': pressDelay} 24 | request_url = self._build_request_url('') 25 | response = self._post(request_url, data) 26 | res = json.loads(response.text) 27 | self._session_id = res['sessionId'] 28 | 29 | def send_launch_channel(self, channel_code: str, contentID, mediaType): 30 | data = {'channelId' : channel_code, 'contentId': contentID, 'contentType': mediaType} 31 | request_url = self._build_request_url(f"/{self._session_id}/launch") 32 | return self._post(request_url, data) 33 | 34 | def send_input_data(self, channelId, contentID, mediaType): 35 | data = {'channelId': channelId, 'contentId': contentID, 'contentType': mediaType} 36 | request_url = self._build_request_url(f"/{self._session_id}/input") 37 | return self._post(request_url, data) 38 | 39 | def get_device_info(self): 40 | request_url = self._build_request_url(f"/{self._session_id}") 41 | return self._get(request_url) 42 | 43 | def side_load(self, form): 44 | request_url = self._build_request_url(f"/{self._session_id}/load") 45 | return requests.post(request_url, files=form) 46 | 47 | def get_player_info(self): 48 | request_url = self._build_request_url(f"/{self._session_id}/player") 49 | return self._get(request_url) 50 | 51 | def send_install_channel(self, channel_code: str): 52 | data = {'channelId' : channel_code} 53 | request_url = self._build_request_url(f"/{self._session_id}/install") 54 | return self._post(request_url, data) 55 | 56 | def send_sequence(self, sequence): 57 | data = {'button_sequence' : sequence} 58 | request_url = self._build_request_url(f"/{self._session_id}/press") 59 | return self._post(request_url, data) 60 | 61 | def get_ui_element(self, data: object): 62 | request_url = self._build_request_url(f"/{self._session_id}/element") 63 | return self._post(request_url, data) 64 | 65 | def set_timeouts(self, timeoutType: str, delay: int): 66 | print(str(delay)) 67 | data = {'type': timeoutType, 'ms': delay} 68 | request_url = self._build_request_url(f"/{self._session_id}/timeouts") 69 | return self._post(request_url, data) 70 | 71 | def get_ui_elements(self, data: object): 72 | request_url = self._build_request_url(f"/{self._session_id}/elements") 73 | return self._post(request_url, data) 74 | 75 | def get_apps(self): 76 | request_url = self._build_request_url(f"/{self._session_id}/apps") 77 | return self._get(request_url) 78 | 79 | def get_current_app(self): 80 | request_url = self._build_request_url(f"/{self._session_id}/current_app") 81 | return self._get(request_url) 82 | 83 | def get_screen_source(self): 84 | request_url = self._build_request_url(f"/{self._session_id}/source") 85 | return self._get(request_url) 86 | 87 | def send_keypress(self, key_press: str): 88 | data = {'button' : key_press} 89 | request_url = self._build_request_url(f"/{self._session_id}/press") 90 | return self._post(request_url, data) 91 | 92 | def get_active_element(self): 93 | request_url = self._build_request_url(f"/{self._session_id}/element/active") 94 | return self._post(request_url, {}) 95 | 96 | def _build_request_url(self, endpoint: str): 97 | return f"http://localhost:9000/v1/session{endpoint}" 98 | 99 | def quiet(self): 100 | request_url = self._build_request_url(f"/{self._session_id}") 101 | return self._delete(request_url) 102 | 103 | def _post(self, request_url: str, data: object): 104 | return requests.post(url = request_url, data = json.dumps(data)) 105 | 106 | def _get(self, request_url: str): 107 | return requests.get(request_url) 108 | 109 | def _delete(self, request_url: str): 110 | return requests.delete(request_url) 111 | 112 | -------------------------------------------------------------------------------- /RobotLibrary/Tests/Basic_tests.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Basic smoke tests 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{DATA2}= using=text value=Barack Gates, Bill Obama 26 | @{DATA2Array}= &{DATA2} 27 | &{Params2}= elementData=${DATA2Array} 28 | &{DATA3}= using=text value=Please enter your username 29 | @{DATA3Array}= &{DATA3} 30 | &{Params3}= elementData=${DATA3Array} 31 | &{DATA4}= using=text value=Please enter your password 32 | @{DATA4Array}= &{DATA4} 33 | &{Params4}= elementData=${DATA4Array} 34 | @{KEYS}= down down down down select 35 | &{DATA5}= using=text value=Authenticate to watch 36 | @{DATA5Array}= &{DATA5} 37 | &{Params5}= elementData=${DATA5Array} 38 | 39 | *** Test Cases *** 40 | Channel should be launched 41 | Side load ../sample/channel.zip rokudev aaaa 42 | Verify is channel loaded ${channel_code} 43 | 44 | Check if details screen showed 45 | Send key select 4 46 | Verify is screen loaded ${Params2} 47 | 48 | Check if playback started 49 | ${status} ${value}= Run Keyword And Ignore Error Verify is screen loaded ${Params5} 2 50 | Run keyword if "${status}"=="PASS" Do auth 51 | ... ELSE Send key select 52 | Verify is playback started 20 2 53 | 54 | *** Keywords *** 55 | Do auth 56 | Send key select 57 | Verify is screen loaded ${Params3} 58 | Send word user 59 | Send keys ${KEYS} 60 | Verify is screen loaded ${Params4} 61 | Send word pass 62 | Send keys ${KEYS} 63 | -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_3_Grid.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{GridData}= using=tag value=GridView 26 | @{GridArray}= &{GridData} 27 | &{GridParams}= elementData=${GridArray} 28 | &{PosterData}= using=attr attribute=name value=poster 29 | @{PosterArray}= &{PosterData} 30 | &{PosterParams}= elementData=${PosterArray} 31 | ${poster1}= https://roku-blog.s3.amazonaws.com/developer/files/2017/04/Roku-Recommends-thumbnail.png 32 | ${poster2}= https://blog.roku.com/developer/files/2016/10/twitch-poster-artwork.png 33 | 34 | *** Test Cases *** 35 | Verify is channel launched 36 | Side load ../channels/3_Grid.zip rokudev aaaa 37 | Verify is channel loaded ${channel_code} 38 | 39 | Verify is initial screen loaded 40 | Verify is screen loaded ${GridParams} 41 | 42 | Verify posters 43 | @{elements}= Get elements ${PosterParams} 4 44 | :FOR ${ELEMENT} IN @{elements[0]['Attrs']} 45 | \ Run Keyword If "${ELEMENT['Name']['Local']}" == "uri" and "${ELEMENT['Value']}" != "${poster1}" Fail 46 | :FOR ${ELEMENT} IN @{elements[1]['Attrs']} 47 | \ Run Keyword If "${ELEMENT['Name']['Local']}" == "uri" and "${ELEMENT['Value']}" != "${poster2}" Fail -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_4_DetailsScreen.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{SeriesDetails}= using=text value=No Content to play 26 | @{SeriesDetailsArray}= &{SeriesDetails} 27 | &{SeriesDetailsParams}= elementData=${SeriesDetailsArray} 28 | &{Grid}= using=tag value=GridView 29 | @{GridArray}= &{Grid} 30 | &{GridParams}= elementData=${GridArray} 31 | &{MovieDetails}= using=text value=Play 32 | @{MovieDetailsArray}= &{MovieDetails} 33 | &{MovieDetailsParams}= elementData=${MovieDetailsArray} 34 | 35 | *** Test Cases *** 36 | Verify is channel launched 37 | Side load ../channels/4_DetailsScreen.zip rokudev aaaa 38 | Verify is channel loaded ${channel_code} 39 | 40 | Verify is initial screen loaded 41 | Verify is screen loaded ${GridParams} 42 | 43 | Verify series details screen button 44 | Send key Select 3 45 | Verify is screen loaded ${SeriesDetailsParams} 3 4 46 | 47 | Verify movies details screen button 48 | Send key Back 3 49 | Verify is screen loaded ${GridParams} 50 | Send key Down 3 51 | Send key Select 3 52 | Verify is screen loaded ${MovieDetailsParams} -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_6_Encards.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{GridData}= using=tag value=GridView 26 | @{GridArray}= &{GridData} 27 | &{GridParams}= elementData=${GridArray} 28 | &{DetailsData}= using=tag value=DetailsView 29 | @{DetailsArray}= &{DetailsData} 30 | &{DetailsParams}= elementData=${DetailsArray} 31 | &{EncardData}= using=text value=Play again 32 | @{EncardArray}= &{EncardData} 33 | &{EncardParams}= elementData=${EncardArray} 34 | 35 | 36 | *** Test Cases *** 37 | Verify is channel launched 38 | Side load ../channels/6_Endcards.zip rokudev aaaa 39 | Verify is channel loaded ${channel_code} 40 | 41 | Verify is initial screen loaded 42 | Verify is screen loaded ${GridParams} 43 | 44 | Verify is details screen loaded 45 | Send key Select 4 46 | Verify is screen loaded ${DetailsParams} 47 | 48 | Verify is playback started 49 | Send key Select 3 50 | Verify is playback started 51 | 52 | Encards 53 | Verify is screen loaded ${EncardParams} 10 10 -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_7_EpisodePickerScreen.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{GridData}= using=tag value=GridView 26 | @{GridArray}= &{GridData} 27 | &{GridParams}= elementData=${GridArray} 28 | &{DetailsData}= using=tag value=DetailsView 29 | @{DetailsArray}= &{DetailsData} 30 | &{DetailsParams}= elementData=${DetailsArray} 31 | &{EncardData}= using=text value=Play again 32 | @{EncardArray}= &{EncardData} 33 | &{EncardParams}= elementData=${EncardArray} 34 | &{CategoryData}= using=tag value=CategoryListView 35 | @{CategoryArray}= &{CategoryData} 36 | &{CategoryParams}= elementData=${CategoryArray} 37 | &{tagData}= using=tag value=RenderableNode 38 | &{attrData}= using=attr attribute=focused value=true 39 | &{season1Data}= using=attr attribute=index value=0 40 | &{season2Data}= using=attr attribute=index value=1 41 | @{Season1Array}= &{tagData} &{attrData} &{season1Data} 42 | &{Season1Params}= elementData=${Season1Array} 43 | @{Season2Array}= &{tagData} &{attrData} &{season2Data} 44 | &{Season2Params}= elementData=${Season2Array} 45 | @{KEYS}= Down Down Down Down Down Down 46 | 47 | *** Test Cases *** 48 | Verify is channel launched 49 | Side load ../channels/7_EpisodePickerScreen.zip rokudev aaaa 50 | Verify is channel loaded ${channel_code} 51 | 52 | Verify is initial screen loaded 53 | Verify is screen loaded ${GridParams} 54 | 55 | Verify is details screen loaded 56 | Send key Select 4 57 | Verify is screen loaded ${DetailsParams} 58 | 59 | Verify is Categort list started 60 | Send key Select 4 61 | Verify is screen loaded ${CategoryParams} 62 | 63 | Verify is Season1 is focused 64 | Verify is screen loaded ${Season1Params} 65 | 66 | Verify is Season2 is focused 67 | Send keys ${KEYS} 68 | Verify is screen loaded ${Season2Params} -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_7_EpisodePicker_focus.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{GridData}= using=tag value=GridView 26 | @{GridArray}= &{GridData} 27 | &{GridParams}= elementData=${GridArray} 28 | &{DetailsData}= using=tag value=DetailsView 29 | @{DetailsArray}= &{DetailsData} 30 | &{DetailsParams}= elementData=${DetailsArray} 31 | &{EncardData}= using=text value=Play again 32 | @{EncardArray}= &{EncardData} 33 | &{EncardParams}= elementData=${EncardArray} 34 | &{CategoryData}= using=tag value=CategoryListView 35 | @{CategoryArray}= &{CategoryData} 36 | &{CategoryParams}= elementData=${CategoryArray} 37 | @{GridKeys}= Up Select 38 | 39 | *** Test Cases *** 40 | Verify is channel launched 41 | Side load ../channels/7_EpisodePickerScreen.zip rokudev aaaa 42 | Verify is channel loaded ${channel_code} 43 | 44 | Verify is initial screen loaded 45 | Verify is screen loaded ${GridParams} 46 | 47 | Verify focused element on grid screen 48 | Send key Down 4 49 | &{focusedEl}= get focusedElement 50 | @{Nodes}= Get From Dictionary ${focusedEl} Nodes 51 | ${uri}= Get attribute @{Nodes}[${1}] uri 52 | Run keyword if '${uri}'!='https://blog.roku.com/developer/files/2016/10/twitch-poster-artwork.png' Fail 53 | 54 | Verify is details screen loaded 55 | Send keys ${GridKeys} 4 56 | Verify is screen loaded ${DetailsParams} 57 | 58 | Verify focused element on details screen 59 | &{focusedEl}= get focusedElement 60 | @{Nodes}= Get From Dictionary ${focusedEl} Nodes 61 | @{Nodes}= Get From Dictionary @{Nodes}[${0}] Nodes 62 | ${text}= Get Attribute @{Nodes}[${2}] text 63 | Run keyword if '${text}'!='Episodes' Fail 64 | 65 | Verify is Categort list started 66 | Send key Select 4 67 | Verify is screen loaded ${CategoryParams} 68 | 69 | Verify focused element on episodes screen (episodes list) 70 | Send key Down 71 | &{focusedEl}= get focusedElement 72 | @{Nodes}= Get From Dictionary ${focusedEl} Nodes 73 | @{Nodes}= Get From Dictionary @{Nodes}[${1}] Nodes 74 | ${uri}= Get Attribute @{Nodes}[${0}] uri 75 | Run keyword if '${uri}'!='https://blog.roku.com/developer/files/2016/10/ted-poster-artwork.png' Fail 76 | 77 | Verify focused element on episodes screen (seasons list) 78 | Send key Left 79 | &{focusedEl}= get focusedElement 80 | @{Nodes}= Get From Dictionary ${focusedEl} Nodes 81 | @{Nodes}= Get From Dictionary @{Nodes}[${0}] Nodes 82 | ${text}= Get Attribute @{Nodes}[${2}] text 83 | Run keyword if '${text}'!='Season${SPACE*2}1' Fail 84 | -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_7_EpisodePicker_utils.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{GridData}= using=tag value=GridView 26 | @{GridArray}= &{GridData} 27 | &{OverhangData}= using=tag value=Overhang 28 | @{OverhangArray}= &{OverhangData} 29 | &{OVerhangParams}= elementData=${OverhangArray} 30 | &{GridParams}= elementData=${GridArray} 31 | &{DetailsData}= using=tag value=DetailsView 32 | @{DetailsArray}= &{DetailsData} 33 | &{DetailsParams}= elementData=${DetailsArray} 34 | &{PosterData}= using=tag value=Poster 35 | &{LabelData}= using=text value=Live Gaming 36 | &{IndexData}= using=attr attribute=index value=1 37 | @{LabelArray}= &{LabelData} &{IndexData} 38 | @{ParamArray}= &{PosterData} 39 | 40 | *** Test Cases *** 41 | Verify is channel launched 42 | Side load ../channels/7_EpisodePickerScreen.zip rokudev aaaa 43 | Verify is channel loaded ${channel_code} 44 | 45 | Verify is initial screen loaded 46 | Verify is screen loaded ${GridParams} 47 | 48 | Verify focused element on grid screen 49 | Send key Down 4 50 | &{focusedEl}= get focusedElement 51 | @{Nodes}= Get child nodes ${focusedEl} ${ParamArray} 52 | Log ${Nodes} 53 | ${uri}= Get attribute @{Nodes}[${0}] uri 54 | Run keyword if '${uri}'!='https://blog.roku.com/developer/files/2016/10/twitch-poster-artwork.png' Fail 55 | 56 | Verify is details screen loaded 57 | Send key Select 2 58 | Verify is screen loaded ${DetailsParams} 59 | 60 | Verify title on details screen 61 | &{Overhang}= get element ${OVerhangParams} 62 | @{Nodes}= Get child nodes ${Overhang} ${LabelArray} 63 | Log ${Nodes} 64 | ${count}= Get length ${Nodes} 65 | should be equal as numbers ${count} 1 -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_9_Bookmarks.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{DetailsData}= using=tag value=DetailsView 26 | @{DetailsArray}= &{DetailsData} 27 | &{DetailsParam}= elementData=@{DetailsArray} 28 | @{KEYS}= Down Select 29 | 30 | *** Test Cases *** 31 | Verify is channel launched 32 | Side load ../channels/9_Bookmarks.zip rokudev aaaa 33 | Verify is channel loaded ${channel_code} 34 | 35 | Verify is details screen loaded 36 | Verify is screen loaded ${DetailsParam} 37 | 38 | Verify is playback started 39 | Send key Select 2 40 | Verify is playback started 41 | 42 | Bookmarks 43 | Sleep 12 44 | Send key Back 45 | Verify is screen loaded ${DetailsParam} 46 | Send keys ${KEYS} 3 47 | Verify is playback started 48 | &{player}= Get player info 49 | Run keyword if ${player['Position']} < 10000 Fail 50 | -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_ParagraphView.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{ParagraphViewData}= using=tag value=ParagraphView 26 | @{ParagraphViewArray}= &{ParagraphViewData} 27 | &{ParagraphViewParams}= elementData=${ParagraphViewArray} 28 | &{HeaderColorData}= using=attr attribute=color value=#22ffffff 29 | &{HeaderTextData}= using=text value=Header Text 30 | @{HeaderArray}= &{HeaderColorData} &{HeaderTextData} 31 | &{HeaderParams}= elementData=${HeaderArray} 32 | &{CodeIndexData}= using=attr attribute=index value=4 33 | &{CodeColorData}= using=attr attribute=color value=#ffff22ff 34 | @{CodeArray}= &{CodeColorData} &{CodeIndexData} 35 | &{CodeParams}= elementData=${CodeArray} 36 | 37 | *** Test Cases *** 38 | Verify is channel launched 39 | Side load ../channels/ParagraphView.zip rokudev aaaa 40 | Verify is channel loaded ${channel_code} 41 | 42 | Verify is initial screen loaded 43 | Verify is screen loaded ${ParagraphViewParams} 44 | 45 | Verify header color 46 | Verify is screen loaded ${HeaderParams} 47 | 48 | Verify reload linking code 49 | &{element}= Get element ${CodeParams} 50 | ${code}= Get attribute ${element} text 51 | Send key Select 52 | &{element2}= Get element ${CodeParams} 53 | ${newCode}= Get attribute ${element2} text 54 | Run Keyword If "${code}" == "${newCode}" Fail -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_Roku_Recommends.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | *** Variables *** 23 | ${channel_code} dev 24 | &{DATA2}= using=text value=No Content to play 25 | @{Params2}= &{DATA2} 26 | &{Grid}= using=tag value=GridView 27 | @{GridArray}= &{Grid} 28 | &{GridParams}= elementData=${GridArray} 29 | &{Row}= using=tag value=Row 30 | @{RowArray}= &{Row} 31 | &{RowParams}= elementData=${RowArray} 32 | 33 | 34 | *** Test Cases *** 35 | Verify is channel launched 36 | Side load ../channels/Roku_Recommends.zip rokudev aaaa 37 | Verify is channel loaded ${channel_code} 38 | 39 | Verify is initial screen loaded 40 | Verify is screen loaded ${GridParams} 41 | 42 | Should be 3 Rows on Grid initially 43 | @{elements}= Get elements ${RowParams} 2 44 | ${len}= Get length ${elements} 45 | should be equal as numbers ${len} 3 46 | 47 | Should be 4 Rows on Grid after Down key press 48 | Send key Down 49 | @{elements}= Get elements ${RowParams} 2 50 | ${len}= Get length ${elements} 51 | should be equal as numbers ${len} 4 -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_SearchView.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{SearchViewData}= using=tag value=SearchView 26 | @{SearchViewArray}= &{SearchViewData} 27 | &{SearchViewParams}= elementData=${SearchViewArray} 28 | &{PlaceholderTextData}= using=text value=Enter search term 29 | @{PlaceholderArray}= &{PlaceholderTextData} 30 | &{PlaceholderParams}= elementData=${PlaceholderArray} 31 | &{RowData}= using=tag value=Row 32 | @{RowDataArray}= &{RowData} 33 | &{RowParams}= elementData=${RowDataArray} 34 | &{ParentTagData}= using=tag value=TextEditBox 35 | @{ParentArray}= &{ParentTagData} 36 | &{ElementTagData}= using=tag value=Label 37 | @{ElementArray}= &{ElementTagData} 38 | &{LabelParams}= elementData=${ElementArray} parentData=${ParentArray} 39 | ${input}= hello@1 r~ 40 | 41 | *** Test Cases *** 42 | Verify is channel launched 43 | Side load ../channels/SearchView.zip rokudev aaaa 44 | Verify is channel loaded ${channel_code} 45 | 46 | Verify is search screen loaded 47 | Verify is screen loaded ${SearchViewParams} 48 | 49 | Verify search input 50 | Verify is screen loaded ${PlaceholderParams} 51 | Send word ${input} 52 | &{label}= Get Element ${LabelParams} 53 | ${text}= Get attribute ${label} text 54 | Run Keyword If "${text}" != "${input}" Fail -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_TimeGridView.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{TimeGridViewData}= using=tag value=TimeGridView 26 | @{TimeGridViewArray}= &{TimeGridViewData} 27 | &{TimeGridViewParams}= elementData=${TimeGridViewArray} 28 | &{ParentTagData}= using=tag value=ChannelRow 29 | &{ParentIndexData}= using=attr attribute=index value=1 30 | @{ParentArray}= &{ParentTagData} &{ParentIndexData} 31 | &{ElementTagData}= using=tag value=Label 32 | @{ElementArray}= &{ElementTagData} 33 | &{LabelParams}= elementData=${ElementArray} parentData=${ParentArray} 34 | ${LabelValue}= KTVK-SD 3.2 35 | 36 | *** Test Cases *** 37 | Verify is channel launched 38 | Side load ../channels/TimeGridView.zip rokudev aaaa 39 | Verify is channel loaded ${channel_code} 40 | 41 | Verify is search screen loaded 42 | Verify is screen loaded ${TimeGridViewParams} 43 | 44 | Verify Label text 45 | &{element}= GetElement ${LabelParams} 46 | ${value}= Get attribute ${element} text 47 | Run Keyword If "${value}" != "${LabelValue}" Fail 48 | -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_audio_mode.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{GridData}= using=tag value=GridView 26 | @{GridArray}= &{GridData} 27 | &{GridParams}= elementData=${GridArray} 28 | &{DetailsData}= using=tag value=DetailsView 29 | @{DetailsArray}= &{DetailsData} 30 | &{DetailsParams}= elementData=${DetailsArray} 31 | &{EncardData}= using=text value=Play again 32 | @{EncardArray}= &{EncardData} 33 | &{EncardParams}= elementData=${EncardArray} 34 | &{CategoryData}= using=tag value=CategoryListView 35 | @{CategoryArray}= &{CategoryData} 36 | &{CategoryParams}= elementData=${CategoryArray} 37 | &{MediaViewData}= using=tag value=MediaView 38 | @{MediaViewArray}= &{MediaViewData} 39 | &{MediaViewParams}= elementData=${MediaViewArray} 40 | &{Item2Data}= using=text value=Item 1.2 41 | @{Item2Array}= &{Item2Data} 42 | &{Item2Params}= elementData=${Item2Array} 43 | @{KEYS}= Fwd Fwd Fwd Fwd Fwd 44 | 45 | *** Test Cases *** 46 | Verify is channel launched 47 | Side load ../channels/audio_mode.zip rokudev aaaa 48 | Verify is channel loaded ${channel_code} 49 | 50 | Verify is initial screen loaded 51 | Verify is screen loaded ${GridParams} 52 | 53 | Verify is details screen loaded 54 | Send key Select 4 55 | Verify is screen loaded ${DetailsParams} 56 | 57 | Verify is MediaView loaded 58 | Send key Select 4 59 | Verify is screen loaded ${MediaViewParams} 60 | 61 | Verify next content after FF 62 | Send keys ${KEYS} 4 63 | Verify is screen loaded ${Item2Params} 6 3 64 | -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_deepLinking.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Deep Linking 3 | Variables ./../Library/variables.py 4 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 5 | Library Collections 6 | 7 | 8 | *** Variables *** 9 | ${channel_code} dev 10 | ${content_id}= decbe34b64ea4ca281dc09997d0f23fd 11 | ${mediaType}= episode 12 | 13 | *** Test Cases *** 14 | Side load 15 | Side load ../channels/Roku_Recommends.zip rokudev aaaa 16 | Send key Home 17 | 18 | Verify is channel launched 19 | Launch the channel ${channel_code} ${content_id} ${mediaType} 20 | Verify is channel loaded ${channel_code} 21 | 22 | Verify is playback started 23 | Verify is playback started 25 2 -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_input.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Deep Linking 3 | Variables ./../Library/variables.py 4 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 5 | Library Collections 6 | 7 | 8 | *** Variables *** 9 | ${channel_code} dev 10 | ${content_id}= decbe34b64ea4ca281dc09997d0f23fd 11 | ${mediaType}= episode 12 | 13 | *** Test Cases *** 14 | Side load 15 | Side load ../channels/Roku_Recommends.zip rokudev aaaa 16 | 17 | Verify is playback started 18 | Input deep linking data ${channel_code} ${content_id} ${mediaType} 19 | Verify is playback started 25 2 -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_timer.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Deep Linking 3 | Variables ./../Library/variables.py 4 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 5 | Library Collections 6 | 7 | 8 | *** Variables *** 9 | ${channel_code} dev 10 | ${content_id}= decbe34b64ea4ca281dc09997d0f23fd 11 | ${content_id_input}= 6c9d0951d6d74229afe4adf972b278dd 12 | ${mediaType}= episode 13 | 14 | *** Test Cases *** 15 | Side load 16 | Side load ../channels/Roku_Recommends.zip rokudev aaaa 17 | Send key Home 18 | 19 | Verify is channel launched 20 | Launch the channel ${channel_code} ${content_id} ${mediaType} 21 | Mark timer 22 | Verify is channel loaded ${channel_code} 23 | 24 | Verify is playback started (Deep linking) 25 | Verify is playback started 26 | 27 | Verify is playback started quickly 28 | ${time_dl}= Get timer 29 | Run Keyword If ${time_dl} > 12000 Fail 30 | 31 | Verify is playback started (input) 32 | Send key Stop 33 | Sleep 2 34 | Mark timer 35 | Input deep linking data ${channel_code} ${content_id_input} ${mediaType} 36 | Verify is playback started 25 2 37 | 38 | Verify is playback(input) started quickly 39 | ${time_in}= Get timer 40 | Run Keyword If ${time_in} > 12000 Fail 41 | -------------------------------------------------------------------------------- /RobotLibrary/Tests/test_video_preloading.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Variables ./../Library/variables.py 19 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 20 | Library Collections 21 | 22 | 23 | *** Variables *** 24 | ${channel_code} dev 25 | &{GridData}= using=tag value=GridView 26 | @{GridArray}= &{GridData} 27 | &{GridParams}= elementData=${GridArray} 28 | &{DetailsData}= using=tag value=DetailsView 29 | @{DetailsArray}= &{DetailsData} 30 | &{DetailsParams}= elementData=${DetailsArray} 31 | &{EncardData}= using=text value=Play again 32 | @{EncardArray}= &{EncardData} 33 | &{EncardParams}= elementData=${EncardArray} 34 | 35 | *** Test Cases *** 36 | Verify is channel launched 37 | Side load ../channels/video_preloading.zip rokudev aaaa 38 | Verify is channel loaded ${channel_code} 39 | 40 | Verify is initial screen loaded 41 | Verify is screen loaded ${GridParams} 42 | 43 | Verify is details screen loaded 44 | Send key Select 4 45 | Verify is screen loaded ${DetailsParams} 46 | &{player}= Get player info 47 | Log To Console ${player} 48 | Run keyword if '${player['State']}' != 'startup' Fail 49 | 50 | Verify is playback started 51 | Send key Select 4 52 | Verify is playback started 53 | &{player}= Get player info 54 | Log To Console ${player} 55 | Run keyword if '${player['State']}' != 'play' Fail 56 | 57 | Verify endcard after first video 58 | Verify is screen loaded ${EncardParams} 8 10 59 | 60 | Verify is playback started after 10 seconds 61 | Verify is playback started 3 5 62 | -------------------------------------------------------------------------------- /RobotLibrary/channel.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/RobotLibrary/channel.zip -------------------------------------------------------------------------------- /RobotLibrary/multipleDevices/Basic_tests_multi_device.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Basic smoke tests 18 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} 19 | Library Collections 20 | 21 | 22 | *** Variables *** 23 | ${channel_code} dev 24 | &{DATA2}= using=text value=Barack Gates, Bill Obama 25 | @{DATA2Array}= &{DATA2} 26 | &{Params2}= elementData=${DATA2Array} 27 | &{DATA3}= using=text value=Please enter your username 28 | @{DATA3Array}= &{DATA3} 29 | &{Params3}= elementData=${DATA3Array} 30 | &{DATA4}= using=text value=Please enter your password 31 | @{DATA4Array}= &{DATA4} 32 | &{Params4}= elementData=${DATA4Array} 33 | @{KEYS}= down down down down select 34 | &{DATA5}= using=text value=Authenticate to watch 35 | @{DATA5Array}= &{DATA5} 36 | &{Params5}= elementData=${DATA5Array} 37 | 38 | *** Test Cases *** 39 | Channel should be launched 40 | Launch the channel ${channel_code} 41 | Verify is channel loaded ${channel_code} 42 | 43 | Check if details screen showed 44 | Send key select 4 45 | Verify is screen loaded ${Params2} 46 | 47 | Check if playback started 48 | ${status} ${value}= Run Keyword And Ignore Error Verify is screen loaded ${Params5} 2 49 | Run keyword if "${status}"=="PASS" Do auth 50 | ... ELSE Send key select 51 | Verify is playback started 10 2 52 | 53 | *** Keywords *** 54 | Do auth 55 | Send key select 56 | Verify is screen loaded ${Params3} 57 | Send word user 58 | Send keys ${KEYS} 59 | Verify is screen loaded ${Params4} 60 | Send word pass 61 | Send keys ${KEYS} 62 | -------------------------------------------------------------------------------- /RobotLibrary/multipleDevices/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "devices": { 3 | "Austin": { 4 | "ip_address": "192.168.1.64", 5 | "timeout": 20000, 6 | "pressDelay": 2000 7 | }, 8 | "Tyler": { 9 | "ip_address": "192.168.1.16", 10 | "timeout": 25000, 11 | "pressDelay": 1000 12 | } 13 | }, 14 | "server_path": "D:/projects/go/webDriver/src/main.exe", 15 | "test": "multipleDevices/test_3_Grid_multi_device.robot", 16 | "outputdir": "Results" 17 | } 18 | -------------------------------------------------------------------------------- /RobotLibrary/multipleDevices/multi.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | import sys 18 | import asyncio 19 | import subprocess 20 | import json 21 | from string import Template 22 | import codecs 23 | if sys.platform == "win32": 24 | asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) 25 | 26 | async def read_stream(stream, cb): 27 | while True: 28 | line = await stream.readline() 29 | if line: 30 | cb(line) 31 | else: 32 | break 33 | 34 | 35 | async def stream_subprocess(cmd, stdout_cb, stderr_cb): 36 | try: 37 | process = await asyncio.create_subprocess_exec( 38 | *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE 39 | ) 40 | 41 | await asyncio.wait( 42 | [ 43 | read_stream(process.stdout, stdout_cb), 44 | read_stream(process.stderr, stderr_cb), 45 | ] 46 | ) 47 | rc = await process.wait() 48 | return process.pid, rc 49 | except OSError as e: 50 | return e 51 | 52 | async def prc(*aws): 53 | await asyncio.gather(*aws) 54 | 55 | def execute(*aws): 56 | asyncio.run(prc(*aws)) 57 | 58 | def printer(label): 59 | def pr(*args): 60 | print(label, *args) 61 | 62 | return pr 63 | 64 | def runners(cmds): 65 | for cmd in cmds: 66 | out = printer(f"{cmd['name']}.stdout") 67 | err = printer(f"{cmd['name']}.stderr") 68 | yield stream_subprocess(cmd['command'], out, err) 69 | 70 | def getCmdList(config): 71 | devices = config['devices'] 72 | outputdir = config['outputdir'] 73 | if devices == None: 74 | raise Exception("devices field is empty") 75 | 76 | for device in devices: 77 | cmd = { 78 | 'command': [sys.executable, "-m", "robot.run"], 79 | 'name': device 80 | } 81 | deviceFields = devices[device] 82 | for key in deviceFields: 83 | cmd['command'].extend(['--variable', f'{key}:{deviceFields[key]}']) 84 | cmd['command'].extend(['--outputdir', f'{outputdir}/{device}']) 85 | cmd['command'].append(config['test']) 86 | yield cmd 87 | 88 | def createGeneralFile(config, fileName, title): 89 | devicesList = [] 90 | outputdir = config['outputdir'] 91 | for device in config['devices']: 92 | devicesList.append({"name": device, "src": f'{device}/{fileName}.html'}) 93 | with codecs.open("multipleDevices/template.html", 'r', 'utf-8') as template: 94 | res = Template(template.read()).safe_substitute(content=devicesList, title=title) 95 | with open(f"{outputdir}/multi_device_{fileName}.html", "w") as new_file: 96 | new_file.write(res) 97 | 98 | if __name__ == "__main__": 99 | try: 100 | config_path = sys.argv[1] 101 | with open(config_path) as myfile: 102 | data = json.load(myfile) 103 | if data['server_path'] == None: 104 | raise Exception("server_path field is required") 105 | else: 106 | process = subprocess.Popen(data['server_path']) 107 | cmdList = getCmdList(data) 108 | execute(*runners(cmdList)) 109 | process.kill() 110 | createGeneralFile(data, "log", "Logs") 111 | createGeneralFile(data, "report", "Reports") 112 | except IndexError: 113 | print("Config path is required as first argument") 114 | except FileNotFoundError: 115 | print('This file doesn\'t exist') 116 | except Exception as e: 117 | print(e) 118 | -------------------------------------------------------------------------------- /RobotLibrary/multipleDevices/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 47 | 48 | 49 |

$title

50 | 51 |
52 |
53 | 54 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /RobotLibrary/multipleDevices/test_3_Grid_multi_device.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | *** Settings *** 17 | Documentation Test 2 18 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} 19 | Library Collections 20 | 21 | 22 | *** Variables *** 23 | ${channel_code} dev 24 | &{GridData}= using=tag value=GridView 25 | @{GridArray}= &{GridData} 26 | &{GridParams}= elementData=${GridArray} 27 | &{PosterData}= using=attr attribute=name value=poster 28 | @{PosterArray}= &{PosterData} 29 | &{PosterParams}= elementData=${PosterArray} 30 | ${poster1}= https://roku-blog.s3.amazonaws.com/developer/files/2017/04/Roku-Recommends-thumbnail.png 31 | ${poster2}= https://blog.roku.com/developer/files/2016/10/twitch-poster-artwork.png 32 | 33 | *** Test Cases *** 34 | Verify is channel launched 35 | Launch the channel ${channel_code} 36 | Verify is channel loaded ${channel_code} 37 | 38 | Verify is initial screen loaded 39 | Verify is screen loaded ${GridParams} 40 | 41 | Verify posters 42 | @{elements}= Get elements ${PosterParams} 4 43 | ${realPoster1}= Get attribute ${elements[0]} uri 44 | Run Keyword If "${realPoster1}" != "${poster1}" Fail 45 | ${realPoster2}= Get attribute ${elements[1]} uri 46 | Run Keyword If "${realPoster2}" != "${poster2}" Fail -------------------------------------------------------------------------------- /RobotLibrary/requirements.txt: -------------------------------------------------------------------------------- 1 | # Robot framework 2 | robotframework==3.1.2 3 | 4 | # Roku WebDriver API calls 5 | requests==2.22.0 6 | -------------------------------------------------------------------------------- /RobotLibrary/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | name="RokuRobotLibrary", 5 | version="2.2.0", 6 | packages=['Library'], 7 | install_requires=[ 8 | 'robotframework==3.1.2', 9 | 'requests==2.22.0' 10 | ] 11 | ) -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_1.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | 27 | *** Test Cases *** 28 | Just launch the channel 29 | Launch the channel ${channel_code} 30 | 31 | -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_10.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Right Select 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | ${Title}= Roku Recommends 31 | &{DetailsData}= using=tag value=DetailsView 32 | @{DetailsArray}= &{DetailsData} 33 | &{DetailsParam}= elementData=@{DetailsArray} 34 | &{TextData}= using=text value=The Paula Deen Channel 35 | @{TextArray}= &{TextData} 36 | &{TextParam}= elementData=@{TextArray} 37 | ${validColor}= "#ddddddff" 38 | &{AdData}= using=text value=Ad 1 of 1 39 | @{AdArray}= &{AdData} 40 | &{AdParam}= elementData=@{AdArray} 41 | &{LabelData}= using=tag value=LabelList 42 | &{PlayData}= using=text value=Play 43 | @{PlayArray}= &{PlayData} 44 | @{LabelArray}= &{LabelData} 45 | &{PlayParam}= elementData=@{PlayArray} parentData=@{LabelArray} 46 | 47 | *** Test Cases *** 48 | launch the channel and verify 49 | Launch the channel ${channel_code} 50 | Verify is channel loaded ${channel_code} 51 | 52 | Verify is initial screen loaded 53 | Verify is screen loaded ${GridParams} 54 | 55 | Get info about current channel and verify Title 56 | &{app}= Get current channel info 57 | Run Keyword If "${Title}" != "${app['Title']}" Fail 58 | 59 | Open details screen 60 | Send keys ${keys} 3 61 | Verify is screen loaded ${DetailsParam} 62 | 63 | Get description element and verify color 64 | &{descriptionElement}= Get element ${TextParam} 65 | ${color}= Get attribute ${descriptionElement} color 66 | Run Keyword If "${validColor}"!="${color}" Fail 67 | 68 | Verify is Ad playback started 69 | Send key Select 70 | Verify is playback started 20 3 71 | Verify is screen loaded ${AdParam} 72 | 73 | Verify is correct Details screen opened after Back key 74 | Send key Back 3 75 | Verify is screen loaded ${PlayParam} -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_11.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Right Select 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | ${Title}= Roku Recommends 31 | &{DetailsData}= using=tag value=DetailsView 32 | @{DetailsArray}= &{DetailsData} 33 | &{DetailsParam}= elementData=@{DetailsArray} 34 | &{TextData}= using=text value=The Paula Deen Channel 35 | @{TextArray}= &{TextData} 36 | &{TextParam}= elementData=@{TextArray} 37 | ${validColor}= "#ddddddff" 38 | &{AdData}= using=text value=Ad 1 of 1 39 | @{AdArray}= &{AdData} 40 | &{AdParam}= elementData=@{AdArray} 41 | &{LabelData}= using=tag value=LabelList 42 | &{PlayData}= using=text value=Play 43 | @{PlayArray}= &{PlayData} 44 | @{LabelArray}= &{LabelData} 45 | &{PlayParam}= elementData=@{PlayArray} parentData=@{LabelArray} 46 | ${content_id}= decbe34b64ea4ca281dc09997d0f23fd 47 | ${mediaType}= episode 48 | 49 | *** Test Cases *** 50 | launch the channel and verify 51 | Launch the channel ${channel_code} 52 | Verify is channel loaded ${channel_code} 53 | 54 | Verify is initial screen loaded 55 | Verify is screen loaded ${GridParams} 56 | 57 | Get info about current channel and verify Title 58 | &{app}= Get current channel info 59 | Run Keyword If "${Title}" != "${app['Title']}" Fail 60 | 61 | Open details screen 62 | Send keys ${keys} 3 63 | Verify is screen loaded ${DetailsParam} 64 | 65 | Get description element and verify color 66 | &{descriptionElement}= Get element ${TextParam} 67 | ${color}= Get attribute ${descriptionElement} color 68 | Run Keyword If "${validColor}"!="${color}" Fail 69 | 70 | Verify is Ad playback started 71 | Send key Select 72 | Verify is playback started 20 3 73 | Verify is screen loaded ${AdParam} 74 | 75 | Verify is correct Details screen opened after Back key 76 | Send key Back 3 77 | Verify is screen loaded ${PlayParam} 78 | 79 | Verify deep linking 80 | Launch the channel ${channel_code} ${content_id} ${mediaType} 81 | Verify is channel loaded ${channel_code} 82 | Verify is playback started 20 3 -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_12.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Right Select 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | ${Title}= Roku Recommends 31 | &{DetailsData}= using=tag value=DetailsView 32 | @{DetailsArray}= &{DetailsData} 33 | &{DetailsParam}= elementData=@{DetailsArray} 34 | &{TextData}= using=text value=The Paula Deen Channel 35 | @{TextArray}= &{TextData} 36 | &{TextParam}= elementData=@{TextArray} 37 | ${validColor}= "#ddddddff" 38 | &{AdData}= using=text value=Ad 1 of 1 39 | @{AdArray}= &{AdData} 40 | &{AdParam}= elementData=@{AdArray} 41 | &{LabelData}= using=tag value=LabelList 42 | &{PlayData}= using=text value=Play 43 | @{PlayArray}= &{PlayData} 44 | @{LabelArray}= &{LabelData} 45 | &{PlayParam}= elementData=@{PlayArray} parentData=@{LabelArray} 46 | ${content_id}= decbe34b64ea4ca281dc09997d0f23fd 47 | ${mediaType}= episode 48 | 49 | *** Test Cases *** 50 | launch the channel and verify 51 | Launch the channel ${channel_code} 52 | Verify is channel loaded ${channel_code} 53 | 54 | Verify is initial screen loaded 55 | Verify is screen loaded ${GridParams} 56 | 57 | Get info about current channel and verify Title 58 | &{app}= Get current channel info 59 | Run Keyword If "${Title}" != "${app['Title']}" Fail 60 | 61 | Open details screen 62 | Send keys ${keys} 3 63 | Verify is screen loaded ${DetailsParam} 64 | 65 | Get description element and verify color 66 | &{descriptionElement}= Get element ${TextParam} 67 | ${color}= Get attribute ${descriptionElement} color 68 | Run Keyword If "${validColor}"!="${color}" Fail 69 | 70 | Verify is Ad playback started 71 | Send key Select 72 | Verify is playback started 20 3 73 | Verify is screen loaded ${AdParam} 74 | 75 | Verify is correct Details screen opened after Back key 76 | Send key Back 3 77 | Verify is screen loaded ${PlayParam} 78 | 79 | Verify deep linking 80 | Mark timer 81 | Launch the channel ${channel_code} ${content_id} ${mediaType} 82 | Verify is channel loaded ${channel_code} 83 | Verify is playback started 20 3 84 | 85 | Verify launch time 86 | ${time_in}= Get timer 87 | Run Keyword If ${time_in} > 40000 Fail -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_13.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Right Select 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | ${Title}= Roku Recommends 31 | &{DetailsData}= using=tag value=DetailsView 32 | @{DetailsArray}= &{DetailsData} 33 | &{DetailsParam}= elementData=@{DetailsArray} 34 | &{TextData}= using=text value=The Paula Deen Channel 35 | @{TextArray}= &{TextData} 36 | &{TextParam}= elementData=@{TextArray} 37 | ${validColor}= "#ddddddff" 38 | &{AdData}= using=text value=Ad 1 of 1 39 | @{AdArray}= &{AdData} 40 | &{AdParam}= elementData=@{AdArray} 41 | &{LabelData}= using=tag value=LabelList 42 | &{PlayData}= using=text value=Play 43 | @{PlayArray}= &{PlayData} 44 | @{LabelArray}= &{LabelData} 45 | &{PlayParam}= elementData=@{PlayArray} parentData=@{LabelArray} 46 | ${content_id}= decbe34b64ea4ca281dc09997d0f23fd 47 | ${mediaType}= episode 48 | 49 | *** Test Cases *** 50 | launch the channel and verify 51 | Launch the channel ${channel_code} 52 | Verify is channel loaded ${channel_code} 53 | 54 | Verify is initial screen loaded 55 | Verify is screen loaded ${GridParams} 56 | 57 | Get info about current channel and verify Title 58 | &{app}= Get current channel info 59 | Run Keyword If "${Title}" != "${app['Title']}" Fail 60 | 61 | Open details screen 62 | Send keys ${keys} 3 63 | Verify is screen loaded ${DetailsParam} 64 | 65 | Get description element and verify color 66 | &{descriptionElement}= Get element ${TextParam} 67 | ${color}= Get attribute ${descriptionElement} color 68 | Run Keyword If "${validColor}"!="${color}" Fail 69 | 70 | Verify is Ad playback started 71 | Send key Select 72 | Verify is playback started 20 3 73 | Verify is screen loaded ${AdParam} 74 | 75 | Verify is correct Details screen opened after Back key 76 | Send key Back 3 77 | Verify is screen loaded ${PlayParam} 78 | 79 | Verify deep linking 80 | Mark timer 81 | Launch the channel ${channel_code} ${content_id} ${mediaType} 82 | Verify is channel loaded ${channel_code} 83 | Verify is playback started 20 3 84 | 85 | Verify launch time 86 | ${time_in}= Get timer 87 | Run Keyword If ${time_in} > 40000 Fail 88 | 89 | Verify input deep linking 90 | Send key Back 3 91 | Sleep 2 92 | Input deep linking data ${channel_code} ${content_id} ${mediaType} 93 | Verify is playback started 20 3 -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_2.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | 27 | *** Test Cases *** 28 | Just launch the channel 29 | Launch the channel ${channel_code} 30 | Verify is channel loaded ${channel_code} 31 | -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_3.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | ${key} Down 27 | 28 | *** Test Cases *** 29 | launch the channel and verify 30 | Launch the channel ${channel_code} 31 | Verify is channel loaded ${channel_code} 32 | 33 | Simulate key press 34 | Send key ${key} -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_4.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Down Down Right 27 | 28 | *** Test Cases *** 29 | launch the channel and verify 30 | Launch the channel ${channel_code} 31 | Verify is channel loaded ${channel_code} 32 | 33 | Simulate key presses 34 | Send keys ${keys} -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_5.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Down Down Right 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | 31 | *** Test Cases *** 32 | launch the channel and verify 33 | Launch the channel ${channel_code} 34 | Verify is channel loaded ${channel_code} 35 | 36 | Verify is initial screen loaded 37 | Verify is screen loaded ${GridParams} -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_6.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Down Down Right 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | ${Title}= Roku Recommends 31 | *** Test Cases *** 32 | launch the channel and verify 33 | Launch the channel ${channel_code} 34 | Verify is channel loaded ${channel_code} 35 | 36 | Verify is initial screen loaded 37 | Verify is screen loaded ${GridParams} 38 | 39 | Get info about current channel and verify Title 40 | &{app}= Get current channel info 41 | Run Keyword If "${Title}" != "${app['Title']}" Fail 42 | 43 | -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_7.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Right Select 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | ${Title}= Roku Recommends 31 | &{DetailsData}= using=tag value=DetailsView 32 | @{DetailsArray}= &{DetailsData} 33 | &{DetailsParams}= elementData=@{DetailsArray} 34 | 35 | *** Test Cases *** 36 | launch the channel and verify 37 | Launch the channel ${channel_code} 38 | Verify is channel loaded ${channel_code} 39 | 40 | Verify is initial screen loaded 41 | Verify is screen loaded ${GridParams} 42 | 43 | Get info about current channel and verify Title 44 | &{app}= Get current channel info 45 | Run Keyword If "${Title}" != "${app['Title']}" Fail 46 | 47 | Open details screen 48 | Send keys ${keys} 49 | Verify is screen loaded ${DetailsParams} 50 | 51 | 52 | -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_8.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2020 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Right Select 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | ${Title}= Roku Recommends 31 | &{DetailsData}= using=tag value=DetailsView 32 | @{DetailsArray}= &{DetailsData} 33 | &{DetailsParam}= elementData=@{DetailsArray} 34 | &{TextData}= using=text value=The Paula Deen Channel 35 | @{TextArray}= &{TextData} 36 | &{TextParam}= elementData=@{TextArray} 37 | ${validColor}= "#ddddddff" 38 | 39 | *** Test Cases *** 40 | launch the channel and verify 41 | Launch the channel ${channel_code} 42 | Verify is channel loaded ${channel_code} 43 | 44 | Verify is initial screen loaded 45 | Verify is screen loaded ${GridParams} 46 | 47 | Get info about current channel and verify Title 48 | &{app}= Get current channel info 49 | Run Keyword If "${Title}" != "${app['Title']}" Fail 50 | 51 | Open details screen 52 | Send keys ${keys} 53 | Verify is screen loaded ${DetailsParam} 54 | 55 | Get description element and verify color 56 | &{descriptionElement}= Get element ${TextParam} 57 | ${color}= Get attribute ${descriptionElement} color 58 | Run Keyword If "${validColor}"!="${color}" Fail 59 | -------------------------------------------------------------------------------- /RobotLibrary/tutorialTests/step_9.robot: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | *** Settings *** 18 | Documentation Test 2 19 | Variables ./../Library/variables.py 20 | Library ./../Library/RobotLibrary.py ${ip_address} ${timeout} ${pressDelay} ${server_path} 21 | Library Collections 22 | 23 | 24 | *** Variables *** 25 | ${channel_code} dev 26 | @{keys} Right Select 27 | &{Grid}= using=tag value=GridView 28 | @{GridArray}= &{Grid} 29 | &{GridParams}= elementData=${GridArray} 30 | ${Title}= Roku Recommends 31 | &{DetailsData}= using=tag value=DetailsView 32 | @{DetailsArray}= &{DetailsData} 33 | &{DetailsParam}= elementData=@{DetailsArray} 34 | &{TextData}= using=text value=The Paula Deen Channel 35 | @{TextArray}= &{TextData} 36 | &{TextParam}= elementData=@{TextArray} 37 | ${validColor}= "#ddddddff" 38 | &{AdData}= using=text value=Ad 1 of 1 39 | @{AdArray}= &{AdData} 40 | &{AdParam}= elementData=@{AdArray} 41 | 42 | *** Test Cases *** 43 | launch the channel and verify 44 | Launch the channel ${channel_code} 45 | Verify is channel loaded ${channel_code} 46 | 47 | Verify is initial screen loaded 48 | Verify is screen loaded ${GridParams} 49 | 50 | Get info about current channel and verify Title 51 | &{app}= Get current channel info 52 | Run Keyword If "${Title}" != "${app['Title']}" Fail 53 | 54 | Open details screen 55 | Send keys ${keys} 3 56 | Verify is screen loaded ${DetailsParam} 57 | 58 | Get description element and verify color 59 | &{descriptionElement}= Get element ${TextParam} 60 | ${color}= Get attribute ${descriptionElement} color 61 | Run Keyword If "${validColor}"!="${color}" Fail 62 | 63 | Verify is Ad playback started 64 | Send key Select 65 | Verify is playback started 20 3 66 | Verify is screen loaded ${AdParam} -------------------------------------------------------------------------------- /RobotLibrary/uTest/_init_.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/RobotLibrary/uTest/_init_.py -------------------------------------------------------------------------------- /RobotLibrary/uTest/response_mock.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | resp = { 18 | "sessionId": "test", 19 | "status": 0, 20 | "value": { 21 | "ip": "1.1.0.127" 22 | } 23 | } 24 | 25 | respWithEmptyValue = { 26 | "sessionId": "test", 27 | "status": 0, 28 | "value": None 29 | } 30 | 31 | respWithApps = { 32 | "sessionId": "test", 33 | "status": 0, 34 | "value": [{ 35 | "Title": "TestChannel", 36 | "ID": "id", 37 | "Type": "menu", 38 | "Version": "1.0.0", 39 | "Subtype": "" 40 | 41 | }] 42 | } 43 | 44 | respWithApp = { 45 | "sessionId": "test", 46 | "status": 0, 47 | "value": { 48 | "Title": "TestChannel", 49 | "ID": "id", 50 | "Type": "menu", 51 | "Version": "1.0.0", 52 | "Subtype": "" 53 | 54 | } 55 | } 56 | 57 | respWithPlayerInfo = { 58 | "sessionId": "42ff6f0d-2362-5e26-a0c9-800e3390bae0", 59 | "status": 0, 60 | "value": { 61 | "Error": "false", 62 | "State": "play", 63 | "Format": { 64 | "Audio": "", 65 | "Captions": "", 66 | "Container": "", 67 | "Drm": "", 68 | "Video": "", 69 | "VideoRes": "" 70 | }, 71 | "Buffering": { 72 | "Current": "", 73 | "Max": "", 74 | "Target": "" 75 | }, 76 | "NewStream": { 77 | "Speed": "" 78 | }, 79 | "Position": "1000 ms", 80 | "Duration": "20000 ms", 81 | "IsLive": "", 82 | "Runtime": "", 83 | "StreamSegment": { 84 | "Bitrate": "", 85 | "MediaSequence": "", 86 | "SegmentType": "", 87 | "Time": "" 88 | } 89 | } 90 | } 91 | 92 | resultPlayerInfo = { 93 | "sessionId": "42ff6f0d-2362-5e26-a0c9-800e3390bae0", 94 | "status": 0, 95 | "value": { 96 | "Error": "false", 97 | "State": "play", 98 | "Format": { 99 | "Audio": "", 100 | "Captions": "", 101 | "Container": "", 102 | "Drm": "", 103 | "Video": "", 104 | "VideoRes": "" 105 | }, 106 | "Buffering": { 107 | "Current": "", 108 | "Max": "", 109 | "Target": "" 110 | }, 111 | "NewStream": { 112 | "Speed": "" 113 | }, 114 | "Position": 1000, 115 | "Duration": 20000, 116 | "IsLive": "", 117 | "Runtime": "", 118 | "StreamSegment": { 119 | "Bitrate": "", 120 | "MediaSequence": "", 121 | "SegmentType": "", 122 | "Time": "" 123 | } 124 | } 125 | } 126 | 127 | uiElement = { 128 | "XMLName": { 129 | "Space": "", 130 | "Local": "Label" 131 | }, 132 | "Attrs": [ 133 | { 134 | "Name": { 135 | "Space": "", 136 | "Local": "bounds" 137 | }, 138 | "Value": "{1, 1, 1, 1}" 139 | }, 140 | { 141 | "Name": { 142 | "Space": "", 143 | "Local": "color" 144 | }, 145 | "Value": "#ffffff6f" 146 | }, 147 | { 148 | "Name": { 149 | "Space": "", 150 | "Local": "index" 151 | }, 152 | "Value": "0" 153 | }, 154 | { 155 | "Name": { 156 | "Space": "", 157 | "Local": "text" 158 | }, 159 | "Value": "text" 160 | } 161 | ], 162 | "Nodes": None 163 | } 164 | 165 | respWithElement = { 166 | "sessionId": "test", 167 | "status": 0, 168 | "value": uiElement 169 | } 170 | 171 | respWithElements = { 172 | "sessionId": "test", 173 | "status": 0, 174 | "value":[uiElement] 175 | } 176 | 177 | respWithDeviceInfo = { 178 | "sessionId": "test", 179 | "status": 0, 180 | "value": { 181 | "ip": "1.1.0.127", 182 | "vendorName": "Roku", 183 | "modelName": "Roku", 184 | "language": "en", 185 | "country": "US" 186 | } 187 | } 188 | 189 | respWithSource = { 190 | "sessionId": "test", 191 | "status": 0, 192 | "value": "dfverv5vrevrf==" 193 | } 194 | 195 | errorMessage = "Error occurs" 196 | 197 | responseWithError = { 198 | "sessionId": "test", 199 | "status": 8, 200 | "value": { 201 | "message": errorMessage 202 | } 203 | } -------------------------------------------------------------------------------- /bin/RokuWebDriver_linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/bin/RokuWebDriver_linux -------------------------------------------------------------------------------- /bin/RokuWebDriver_mac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/bin/RokuWebDriver_mac -------------------------------------------------------------------------------- /bin/RokuWebDriver_win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/bin/RokuWebDriver_win.exe -------------------------------------------------------------------------------- /channels/3_Grid.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/3_Grid.zip -------------------------------------------------------------------------------- /channels/4_DetailsScreen.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/4_DetailsScreen.zip -------------------------------------------------------------------------------- /channels/6_Endcards.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/6_Endcards.zip -------------------------------------------------------------------------------- /channels/7_EpisodePickerScreen.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/7_EpisodePickerScreen.zip -------------------------------------------------------------------------------- /channels/9_Bookmarks.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/9_Bookmarks.zip -------------------------------------------------------------------------------- /channels/ParagraphView.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/ParagraphView.zip -------------------------------------------------------------------------------- /channels/Roku_Recommends.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/Roku_Recommends.zip -------------------------------------------------------------------------------- /channels/SearchView.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/SearchView.zip -------------------------------------------------------------------------------- /channels/TimeGridView.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/TimeGridView.zip -------------------------------------------------------------------------------- /channels/audio_mode.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/audio_mode.zip -------------------------------------------------------------------------------- /channels/video_preloading.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/channels/video_preloading.zip -------------------------------------------------------------------------------- /jsLibrary/library/client.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const axios = require('axios') 17 | const BASE_URL = "http://localhost:9000/v1/session" 18 | 19 | 20 | class Client { 21 | 22 | constructor(ip, timeout, delay) { 23 | this.ip = ip; 24 | this.timeout = timeout; 25 | this.delay = delay; 26 | this.sessionId = ""; 27 | } 28 | 29 | async createSession(ip, timeout, delay) { 30 | return await this.doRequest('', 'post', {'ip' : ip, 'timeout': timeout, 'pressDelay': delay}, {}, false); 31 | } 32 | 33 | async launch(channel_code, contentID, contentType) { 34 | return await this.doRequest('/launch', 'post', {'channelId' : channel_code, 'contentId': contentID, 'contentType': contentType}); 35 | } 36 | 37 | async sendKeypress(key) { 38 | return await this.doRequest('/press', 'post', {'Button' : key}); 39 | } 40 | 41 | async deleteSession() { 42 | return await this.doRequest('', 'delete'); 43 | } 44 | 45 | async getDeviceInfo() { 46 | return await this.doRequest('', 'get'); 47 | } 48 | 49 | async getApps() { 50 | return await this.doRequest('/apps', 'get'); 51 | } 52 | 53 | async getCurrentApp() { 54 | return await this.doRequest('/current_app', 'get'); 55 | } 56 | 57 | async sendSequence(sequence) { 58 | return await this.doRequest('/press', 'post', {'button_sequence' : sequence}); 59 | } 60 | 61 | async sendInputData(channelId, contentID, mediaType) { 62 | return await this.doRequest('/input', 'post', {'channelId': channelId, 'contentId' : contentID, 'contentType': mediaType }); 63 | } 64 | 65 | async getPlayerInfo() { 66 | return await this.doRequest('/player', 'get'); 67 | } 68 | 69 | async getScreenSource() { 70 | return await this.doRequest('/source', 'get'); 71 | } 72 | 73 | async getActiveElement() { 74 | return await this.doRequest('/element/active', 'post'); 75 | } 76 | 77 | async setTimeouts( timeoutType, delay) { 78 | return await this.doRequest('/timeouts', 'post', {'type': timeoutType, 'ms': delay}); 79 | } 80 | 81 | async sendInstallChannel(channelCode) { 82 | return await this.doRequest('/install', 'post', {'channelId' : channelCode}); 83 | } 84 | 85 | async getUiElements(data) { 86 | return await this.doRequest('/elements', 'post', data); 87 | } 88 | 89 | async getUiElement(data) { 90 | return await this.doRequest('/element', 'post', data); 91 | } 92 | 93 | async sideLoadChannel(form) { 94 | console.log 95 | return await this.doRequest('/load', 'post', form, form.getHeaders()); 96 | } 97 | 98 | async addSessionId(url) { 99 | if (this.sessionId == '') { 100 | let result = await this.createSession(this.ip, this.timeout, this.delay); 101 | if (result.status == 200) { 102 | this.sessionId = result.data.sessionId; 103 | } 104 | } 105 | url = `/${this.sessionId}${url}`; 106 | return url; 107 | } 108 | 109 | async doRequest(url, method, body = {}, headers = {}, addSessionId = true) { 110 | try { 111 | if (addSessionId == true) { 112 | url = await this.addSessionId(url); 113 | } 114 | const result = await axios({ 115 | method: method, 116 | url: `${BASE_URL}${url}`, 117 | data: body, 118 | headers: { 119 | ...headers 120 | } 121 | }); 122 | return result; 123 | } catch (errorResponse) { 124 | const response = errorResponse.response; 125 | if (response == undefined) { 126 | throw new Error('Could not get any response'); 127 | } 128 | const status = response.status; 129 | let errorMessage; 130 | if (status == 400) { 131 | errorMessage = response.data; 132 | } else { 133 | errorMessage = response.data.value.message; 134 | } 135 | throw new Error(errorMessage); 136 | } 137 | } 138 | } 139 | 140 | module.exports.Client = Client -------------------------------------------------------------------------------- /jsLibrary/multipleDevices/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "devices": { 3 | "Austin": { 4 | "ip_address": "192.168.1.64", 5 | "timeout": 20000, 6 | "pressDelay": 2000 7 | }, 8 | "Austin2": { 9 | "ip_address": "192.168.1.195", 10 | "timeout": 20000, 11 | "pressDelay": 2000 12 | } 13 | }, 14 | "server_path": "D:/projects/go/webDriver/src/main.exe", 15 | "test": "D:/projects/go/webDriver/jsLibrary/multipleDevices/multiple_devices_test_basics.js", 16 | "outputdir": "Results" 17 | } 18 | -------------------------------------------------------------------------------- /jsLibrary/multipleDevices/multi.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const { spawn } = require('child_process'); 17 | const fs = require("fs"); 18 | 19 | function getCmdList(config) { 20 | const devices = config.devices; 21 | if (devices === undefined) { 22 | throw new Error("devices field is empty"); 23 | } 24 | let cmds = []; 25 | for(const device in devices) { 26 | let deviceFields = devices[device]; 27 | let cmd = { 28 | options: '', 29 | name: device 30 | } 31 | for(const field in deviceFields) { 32 | cmd.options += `--${field}=${deviceFields[field]} `; 33 | } 34 | cmds.push(cmd); 35 | } 36 | return cmds; 37 | } 38 | 39 | function startTest(cmds, config) { 40 | let numberOfProcess = cmds.length; 41 | const {test, outputdir, server_path} = config; 42 | const webDriver = spawn(server_path); 43 | const isWin = process.platform === "win32"; 44 | const executeCmd = isWin ? 'mocha.cmd' : 'mocha'; 45 | cmds.forEach(({options, name}) => { 46 | let child = spawn(executeCmd, [`${test}`,'--reporter', 'mochawesome', '--reporter-options', `reportDir=${outputdir},reportFilename=${name}`, options]); 47 | child.on('error', (messasge) => { 48 | console.log(`${name} error: ${messasge}`); 49 | }); 50 | child.stdout.on('data', (data) => { 51 | console.log(`${name} stdout:\n${data}`); 52 | }); 53 | 54 | child.on('exit', () => { 55 | numberOfProcess--; 56 | if (numberOfProcess === 0) { 57 | webDriver.kill(); 58 | createGeneralReport(config); 59 | } 60 | }); 61 | }); 62 | } 63 | 64 | function createGeneralReport(config) { 65 | let deviceList = []; 66 | const {devices, outputdir} = config; 67 | for(const device in devices) { 68 | deviceList.push({'name': device, 'src': `${device}.html`}); 69 | } 70 | let template = fs.readFileSync('multipleDevices/template.html', 'utf8'); 71 | template = template.replace('$content', JSON.stringify(deviceList)); 72 | template = template.replace('$title', 'Report'); 73 | fs.writeFile(`${outputdir}/multi_device_report.html`, template, (err) => { 74 | if (err) throw err; 75 | console.log(`Multi-device report saved to ${outputdir}/multi_device_report.html`); 76 | }); 77 | } 78 | 79 | function checkRequiredFields(json) { 80 | const fields = ['devices', 'server_path', 'outputdir', 'test']; 81 | fields.forEach( field => { 82 | if (json[field] === undefined) { 83 | throw new Error(`Please, add field \'${field}\' to config`); 84 | } 85 | }); 86 | } 87 | 88 | try { 89 | const args = process.argv; 90 | const configPath = args[2]; 91 | if (configPath === undefined) { 92 | throw new Error('Path to config is required'); 93 | } 94 | const config = fs.readFileSync(configPath); 95 | const jsonData = JSON.parse(config); 96 | checkRequiredFields(jsonData); 97 | const cmdList = getCmdList(jsonData); 98 | startTest(cmdList, jsonData); 99 | } catch (e) { 100 | console.log(e); 101 | } -------------------------------------------------------------------------------- /jsLibrary/multipleDevices/multiple_devices_test_3-Grid.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const {getArgs} = require('./utils'); 19 | let library; 20 | const args = getArgs(); 21 | let ip = args.ip_address || "192.168.1.12"; 22 | describe('Channel should be launched', () => { 23 | before(() => { 24 | library = new rokuLibrary.Library(ip); 25 | }); 26 | 27 | it('should launch the channel', async function() { 28 | this.timeout(25000); 29 | await library.launchTheChannel('dev'); 30 | await library.verifyIsChannelLoaded('dev'); 31 | }); 32 | 33 | it('Verify is initial screen loaded', async function() { 34 | this.timeout(10000); 35 | const res = await library.verifyIsScreenLoaded({ 36 | 'elementData': [{'using': 'tag', 'value': 'GridView'} 37 | ]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify posters', async function() { 42 | this.timeout(50000); 43 | const elements = await library.getElements({'elementData': [{'using': 'attr', 'attribute': 'name', 'value': 'poster'}]}, 4); 44 | let poster = library.getAttribute(elements[0], 'uri'); 45 | expect(poster).to.equal('https://roku-blog.s3.amazonaws.com/developer/files/2017/04/Roku-Recommends-thumbnail.png'); 46 | poster = library.getAttribute(elements[1], 'uri'); 47 | expect(poster).to.equal('https://blog.roku.com/developer/files/2016/10/twitch-poster-artwork.png'); 48 | }); 49 | 50 | after(async () => { 51 | await library.close(); 52 | }); 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /jsLibrary/multipleDevices/multiple_devices_test_basics.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const {getArgs} = require('./utils'); 19 | 20 | let library; 21 | const args = getArgs(); 22 | let ip = args.ip_address || "192.168.1.64"; 23 | let timeout = parseInt(args.timeout) || 20000; 24 | let pressDelay = parseInt(args.pressDelay) || 2000; 25 | console.log(args) 26 | describe('Channel should be launched', () => { 27 | before(() => { 28 | library = new rokuLibrary.Library(ip, timeout, pressDelay); 29 | }); 30 | 31 | it('should launch the channel', async function() { 32 | this.timeout(15000); 33 | await library.launchTheChannel('dev'); 34 | await library.verifyIsChannelLoaded('dev'); 35 | }); 36 | 37 | it('Check if details screen showed', async function() { 38 | this.timeout(30000); 39 | await library.sendKey('select', 4); 40 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Barack Gates, Bill Obama'}]}); 41 | expect(res).to.equal(true); 42 | }); 43 | 44 | it('Check if playback started', async function() { 45 | this.timeout(70000); 46 | let res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Authenticate to watch'}]}, 2, 2); 47 | if (res == true) { 48 | await library.sendKey('select'); 49 | res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Please enter your username'}]}); 50 | if (res == false) { 51 | expect.fail("Can't enter user name"); 52 | } 53 | await library.sendWord('user'); 54 | await library.sendKeys(['down', 'down', 'down', 'down', 'select']); 55 | await library.sendWord('pass'); 56 | await library.sendKeys(['down', 'down', 'down', 'down', 'select']); 57 | } else { 58 | await library.sendKey('select'); 59 | } 60 | res = await library.verifyIsPlaybackStarted(25, 2); 61 | expect(res).to.equal(true); 62 | }); 63 | 64 | after(async () => { 65 | await library.close(); 66 | }); 67 | }); 68 | 69 | -------------------------------------------------------------------------------- /jsLibrary/multipleDevices/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 47 | 48 | 49 |

$title

50 | 51 |
52 |
53 | 54 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /jsLibrary/multipleDevices/utils.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | function getArgs () { 17 | const data = process.argv; 18 | let args = {}; 19 | const length = data.length; 20 | const options = data[length - 1]; 21 | options 22 | .split(' ') 23 | .forEach((argument) => { 24 | if (argument.slice(0,2) === '--') { 25 | const arg = argument.split('='); 26 | const argFlag = arg[0].slice(2,arg[0].length); 27 | const argValue = arg.length > 1 ? arg[1] : true; 28 | args[argFlag] = argValue; 29 | } 30 | }) 31 | return args; 32 | } 33 | 34 | module.exports = { 35 | getArgs: getArgs 36 | } -------------------------------------------------------------------------------- /jsLibrary/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "test": "mocha \"tests/test_basic.js\"" 4 | }, 5 | "dependencies": { 6 | "axios": "0.19.1", 7 | "chai": "4.2.0", 8 | "form-data": "3.0.0", 9 | "mocha": "7.0.1", 10 | "nock": "12.0.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_3-Grid.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_3-Grid', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.121"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/3_Grid.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify posters', async function() { 42 | this.timeout(50000); 43 | const elements = await library.getElements({'elementData': [{'using': 'attr', 'attribute': 'name', 'value': 'poster'}]}, 4); 44 | let poster = library.getAttribute(elements[0], 'uri'); 45 | expect(poster).to.equal('https://roku-blog.s3.amazonaws.com/developer/files/2017/04/Roku-Recommends-thumbnail.png'); 46 | poster = library.getAttribute(elements[1], 'uri'); 47 | expect(poster).to.equal('https://blog.roku.com/developer/files/2016/10/twitch-poster-artwork.png'); 48 | }); 49 | 50 | after(async () => { 51 | await library.close(); 52 | childProcess.kill(); 53 | }); 54 | }); 55 | 56 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_3-Grid_utils.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_3-Grid_utils', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.11"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/3_Grid.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify posters', async function() { 42 | this.timeout(10000); 43 | const element = await library.getFocusedElement(); 44 | const result = library.getChildNodes(element, [{"using": "tag", "value": "poster"}]); 45 | if (result.length > 0) { 46 | const uri = library.getAttribute(result[0], "uri"); 47 | expect(uri).to.equal("https://roku-blog.s3.amazonaws.com/developer/files/2017/04/Roku-Recommends-thumbnail.png") 48 | } else { 49 | throw new Error('Can not find poster'); 50 | } 51 | }); 52 | 53 | it('Verify focused row title', async function() { 54 | this.timeout(10000); 55 | const rowList = await library.getElement({"elementData" :[{"using": "tag", "value": "ZoomRowList"}]}); 56 | const searchData = [{"using": "tag", "value": "RenderableNode"}, {"using": "attr", "attribute": "focused", "value": "true"}]; 57 | const result = library.getChildNodes(rowList, searchData); 58 | if (result.length > 0) { 59 | const renderableNode = result[0]; 60 | const labelSearchData = [{"using": "text", "value": "series"}]; 61 | const labelList = library.getChildNodes(renderableNode, labelSearchData); 62 | expect(labelList.length).to.equal(1); 63 | } else { 64 | throw new Error('Can not find focused renderable node'); 65 | } 66 | }); 67 | 68 | after(async () => { 69 | await library.close(); 70 | childProcess.kill(); 71 | }); 72 | }); 73 | 74 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_4-DetailsScreen.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_4-DetailsScreen', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.11"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/4_DetailsScreen.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify series details screen button', async function() { 42 | this.timeout(50000); 43 | await library.sendKey('select', 3); 44 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'No Content to play'}]}, 3, 4); 45 | expect(res).equal(true); 46 | }); 47 | 48 | it('Verify movies details screen button', async function() { 49 | this.timeout(50000); 50 | await library.sendKeys(['back', 'down', 'select'], 3); 51 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Play'}]}, 3, 4); 52 | expect(res).equal(true); 53 | }); 54 | 55 | after(async () => { 56 | await library.close(); 57 | childProcess.kill(); 58 | }); 59 | }); 60 | 61 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_6-Encards.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_6-Encards', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.11"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/6_Endcards.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify is details screen loaded', async function() { 42 | this.timeout(50000); 43 | await library.sendKey('select', 3); 44 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'DetailsView'}]}); 45 | expect(res).equal(true); 46 | }); 47 | 48 | it('Verify is playback started', async function() { 49 | this.timeout(50000); 50 | await library.sendKey('select', 3); 51 | const res = await library.verifyIsPlaybackStarted(); 52 | expect(res).equal(true); 53 | }); 54 | 55 | it('Encards', async function() { 56 | this.timeout(80000); 57 | await library.sendKeys(['fwd', 'fwd', 'fwd', 'fwd']); 58 | await library.sendKey('select', 3); 59 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Play'}]}, 5, 5); 60 | expect(res).equal(true); 61 | }); 62 | 63 | after(async () => { 64 | await library.close(); 65 | childProcess.kill(); 66 | }); 67 | }); 68 | 69 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_7-EpisodePicker.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_7-EpisodePicker', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.11"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/7_EpisodePickerScreen.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify is details screen loaded', async function() { 42 | this.timeout(50000); 43 | await library.sendKey('select', 3); 44 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'DetailsView'}]}); 45 | expect(res).equal(true); 46 | }); 47 | 48 | it('Verify is Categort list loaded', async function() { 49 | this.timeout(50000); 50 | await library.sendKey('select', 3); 51 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'CategoryListView'}]}); 52 | expect(res).equal(true); 53 | }); 54 | 55 | it('Verify is Season1 is focused', async function() { 56 | this.timeout(10000); 57 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'RenderableNode'}, 58 | {'using': 'attr', 'attribute': 'focused', 'value': 'true'}, 59 | {'using': 'attr', 'attribute': 'index', 'value': '0'} 60 | ]}); 61 | expect(res).equal(true); 62 | }); 63 | 64 | it('Verify is Season2 is focused', async function() { 65 | this.timeout(20000); 66 | await library.sendKeys(['Down', 'Down', 'Down', 'Down', 'Down', 'Down']); 67 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'RenderableNode'}, 68 | {'using': 'attr', 'attribute': 'focused', 'value': 'true'}, 69 | {'using': 'attr', 'attribute': 'index', 'value': '1'} 70 | ]}); 71 | expect(res).equal(true); 72 | }); 73 | 74 | 75 | after(async () => { 76 | await library.close(); 77 | childProcess.kill(); 78 | }); 79 | }); 80 | 81 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_7_EpisodePicker_focus.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | const getChildNodesByTag = (node, name) => { 23 | const children = node.Nodes; 24 | const result = [] 25 | if (children == null) { 26 | return result 27 | } 28 | name = name.toLowerCase() 29 | children.forEach((value) => { 30 | if (value.XMLName.Local.toLowerCase() == name) { 31 | result.push(value) 32 | } 33 | const elements = getChildNodesByTag(value, name) 34 | result.push(...elements) 35 | }) 36 | return result 37 | } 38 | 39 | let library; 40 | 41 | describe('test_7-EpisodePicker_focus', () => { 42 | before(async function() { 43 | this.timeout(20000); 44 | library = new rokuLibrary.Library("192.168.2.11"); 45 | await library.sideLoad("../channels/7_EpisodePickerScreen.zip", "rokudev", "aaaa"); 46 | }); 47 | 48 | it('Check if channel exist on the device', async function() { 49 | this.timeout(5000); 50 | const apps = await library.getApps(); 51 | const res = library.verifyIsChannelExist(apps, 'dev'); 52 | expect(res).equal(true); 53 | }); 54 | 55 | it('Verify is initial screen loaded', async function() { 56 | this.timeout(10000); 57 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 58 | expect(res).to.equal(true); 59 | }); 60 | 61 | it('Verify focused element on grid screen', async function() { 62 | this.timeout(50000); 63 | await library.sendKey('down', 3); 64 | const element = await library.getFocusedElement(); 65 | const children = getChildNodesByTag(element, "poster"); 66 | if (children.length > 0) { 67 | const poster = children[0]; 68 | const posterUri = library.getAttribute(poster, "uri"); 69 | expect(posterUri).to.equal("https://blog.roku.com/developer/files/2016/10/twitch-poster-artwork.png") 70 | } else { 71 | throw new Error("Can't find poster") 72 | } 73 | }); 74 | 75 | it('Verify is details screen loaded', async function() { 76 | this.timeout(50000); 77 | await library.sendKeys(['up','select'], 3); 78 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'DetailsView'}]}); 79 | expect(res).equal(true); 80 | }); 81 | 82 | it('Verify focused element on details screen', async function() { 83 | this.timeout(50000); 84 | const element = await library.getFocusedElement(); 85 | const children = getChildNodesByTag(element, 'label'); 86 | if (children.length > 0) { 87 | const label = children[0]; 88 | const labelText = library.getAttribute(label, 'text'); 89 | expect(labelText).to.equal("Episodes") 90 | } else { 91 | throw new Error("Can't find label") 92 | } 93 | }); 94 | 95 | it('Verify is Categort list loaded', async function() { 96 | this.timeout(50000); 97 | await library.sendKey('select', 3); 98 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'CategoryListView'}]}); 99 | expect(res).equal(true); 100 | }); 101 | 102 | it('Verify focused element on episodes screen (episodes list)', async function() { 103 | this.timeout(50000); 104 | await library.sendKey('down'); 105 | const element = await library.getFocusedElement(); 106 | const children = getChildNodesByTag(element, 'label'); 107 | if (children.length > 0) { 108 | const label = children[0]; 109 | const labelText = library.getAttribute(label, 'text'); 110 | expect(labelText).to.equal("Ideas Worth Spreading") 111 | } else { 112 | throw new Error("Can't find label") 113 | } 114 | }); 115 | 116 | it('Verify focused element on episodes screen (seasons list)', async function() { 117 | this.timeout(50000); 118 | await library.sendKey('left'); 119 | const element = await library.getFocusedElement(); 120 | const children = getChildNodesByTag(element, 'label'); 121 | if (children.length > 0) { 122 | const label = children[0]; 123 | const labelText = library.getAttribute(label, 'text'); 124 | expect(labelText).to.equal("Season 1") 125 | } else { 126 | throw new Error("Can't find label") 127 | } 128 | }); 129 | 130 | after(async () => { 131 | await library.close(); 132 | childProcess.kill(); 133 | }); 134 | }); 135 | 136 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_9-Bookmarks.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_9-Bookmarks', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.11"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/9_Bookmarks.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is details screen loaded', async function() { 36 | this.timeout(50000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'DetailsView'}]}); 38 | expect(res).equal(true); 39 | }); 40 | 41 | it('Verify is playback started', async function() { 42 | this.timeout(50000); 43 | await library.sendKey('select', 3); 44 | const res = await library.verifyIsPlaybackStarted(); 45 | expect(res).equal(true); 46 | }); 47 | 48 | it('Bookmarks', async function() { 49 | this.timeout(50000); 50 | await library.sleep(12000); 51 | await library.sendKey('back', 3); 52 | let res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'DetailsView'}]}); 53 | expect(res).equal(true); 54 | await library.sendKeys(['down', 'select'], 3); 55 | res = await library.verifyIsPlaybackStarted(); 56 | expect(res).equal(true); 57 | let data = await library.getPlayerInfo(); 58 | expect(data.Position).greaterThan(9000); 59 | }); 60 | 61 | after(async () => { 62 | await library.close(); 63 | childProcess.kill(); 64 | }); 65 | }); 66 | 67 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_ParagraphView.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_ParagraphView', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.1.64"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/ParagraphView.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'ParagraphView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify header color', async function() { 42 | this.timeout(50000); 43 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Header Text'}, {'using': 'attr', 'attribute': 'color', 'value': '#22ffffff'}]}); 44 | expect(res).equal(true); 45 | }); 46 | 47 | it('Verify reload linking code', async function() { 48 | this.timeout(50000); 49 | const params = [{'using': 'attr', 'attribute': 'index', 'value': '4'}, {'using': 'attr', 'attribute': 'color', 'value': '#ffff22ff'}]; 50 | let element = await library.getElement({'elementData': params}); 51 | const code = library.getAttribute(element, 'text'); 52 | await library.sendKey('select'); 53 | element = await library.getElement({'elementData': params}); 54 | const newCode = library.getAttribute(element, 'text'); 55 | expect(code).to.not.equal(newCode); 56 | }); 57 | 58 | after(async () => { 59 | await library.close(); 60 | childProcess.kill(); 61 | }); 62 | }); 63 | 64 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_Roku-Recommends.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_Roku-Recommends', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.121"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/Roku_Recommends.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Should be 3 Rows on Grid initially', async function() { 42 | this.timeout(50000); 43 | const params = [ {'using': 'tag','value': 'Row'}]; 44 | const elements = await library.getElements({'elementData': params}); 45 | expect(elements.length).to.equal(3); 46 | }); 47 | 48 | it('Should be 4 Rows on Grid after Down key press', async function() { 49 | this.timeout(50000); 50 | await library.sendKey('down'); 51 | const params = [ {'using': 'tag','value': 'Row'}]; 52 | const elements = await library.getElements({'elementData': params}); 53 | expect(elements.length).to.equal(4); 54 | }); 55 | 56 | after(async () => { 57 | await library.close(); 58 | childProcess.kill(); 59 | }); 60 | }); 61 | 62 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_Roku-Recommends_input.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_Roku_Recommends_input', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.1.11"); 27 | }); 28 | 29 | it('side loading', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/Roku_Recommends.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('input deep linking', async function() { 36 | this.timeout(50000); 37 | await library.sleep(1000); 38 | await library.inputDeepLinkingData('dev', 'decbe34b64ea4ca281dc09997d0f23fd', 'episode'); 39 | library.markTimer(); 40 | await library.sleep(1000); 41 | let res = await library.verifyIsPlaybackStarted(25, 1); 42 | expect(res).to.equal(true); 43 | let time = library.getTimer(); 44 | expect(14000).greaterThan(time); 45 | }); 46 | 47 | after(async () => { 48 | await library.close(); 49 | childProcess.kill(); 50 | }); 51 | }); 52 | 53 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_Roku_Recommends_deepLinking.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_Roku_Recommends_deeplinking', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.1.11"); 27 | }); 28 | 29 | it('side loading', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/Roku_Recommends.zip", "rokudev", "aaaa"); 32 | await library.sendKey('Home'); 33 | }); 34 | 35 | it('should launch the channel', async function() { 36 | this.timeout(5000); 37 | await library.launchTheChannel('dev', 'decbe34b64ea4ca281dc09997d0f23fd', 'episode'); 38 | await library.verifyIsChannelLoaded('dev'); 39 | }); 40 | 41 | it('Verify is playback started', async function() { 42 | this.timeout(50000); 43 | const res = await library.verifyIsPlaybackStarted(20, 2); 44 | expect(res).equal(true); 45 | }); 46 | 47 | after(async () => { 48 | await library.close(); 49 | childProcess.kill(); 50 | }); 51 | }); 52 | 53 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_SearchView.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_SearchView', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.1.11"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/SearchView.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'SearchView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify search input', async function() { 42 | this.timeout(50000); 43 | const input = 'hello@1 r~'; 44 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Enter search term'}]}); 45 | expect(res).to.equal(true); 46 | await library.sendWord(input); 47 | const inputLabel = await library.getElement({ 48 | 'elementData': [{'using': 'tag', 'value': 'Label'}], 49 | 'parentData': [{'using': 'tag', 'value': 'TextEditBox'}] 50 | }); 51 | const text = library.getAttribute(inputLabel, 'text'); 52 | expect(text).to.equal(input); 53 | }); 54 | 55 | after(async () => { 56 | await library.close(); 57 | childProcess.kill(); 58 | }); 59 | }); 60 | 61 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_TimeGridView.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_TimeGridView', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.1.64"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/TimeGridView.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'TimeGridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify Label text', async function() { 42 | this.timeout(10000); 43 | const element = await library.getElement( 44 | {'elementData': [{'using': 'tag', 'value': 'Label'}], 45 | 'parentData': [{'using': 'tag', 'value': 'ChannelRow'}, {'using': 'attr', 'attribute': 'index', 'value': '1'}] 46 | }); 47 | const value = library.getAttribute(element, 'text'); 48 | expect(value).to.equal('KTVK-SD 3.2'); 49 | }); 50 | 51 | after(async () => { 52 | await library.close(); 53 | childProcess.kill(); 54 | }); 55 | }); 56 | 57 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_audio-mode.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_audio-mode', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.11"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/audio_mode.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify is details screen loaded', async function() { 42 | this.timeout(50000); 43 | await library.sendKey('select', 3); 44 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'DetailsView'}]}); 45 | expect(res).equal(true); 46 | }); 47 | 48 | it('Verify is MediaView loaded', async function() { 49 | this.timeout(50000); 50 | await library.sendKey('select', 3); 51 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'MediaView'}]}); 52 | expect(res).equal(true); 53 | }); 54 | 55 | it('Verify next content after FF', async function() { 56 | this.timeout(80000); 57 | await library.sendKeys(['fwd', 'fwd', 'fwd', 'fwd', 'fwd'], 3); 58 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Item 1.2'}]}, 6, 3); 59 | expect(res).equal(true); 60 | }); 61 | 62 | after(async () => { 63 | await library.close(); 64 | childProcess.kill(); 65 | }); 66 | }); 67 | 68 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_basic.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_basic', () => { 25 | before(async function() { 26 | this.timeout(50000); 27 | library = new rokuLibrary.Library("192.168.2.121"); 28 | await library.sideLoad("../sample/channel.zip", "rokudev", "aaaa"); 29 | }); 30 | 31 | it('should launch the channel', async function() { 32 | this.timeout(15000); 33 | await library.verifyIsChannelLoaded('dev'); 34 | }); 35 | 36 | it('Check if details screen showed', async function() { 37 | this.timeout(30000); 38 | await library.sendKey('select', 4); 39 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Barack Gates, Bill Obama'}]}); 40 | expect(res).to.equal(true); 41 | }); 42 | 43 | it('Check if playback started', async function() { 44 | this.timeout(50000); 45 | let res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Authenticate to watch'}]}, 2, 2); 46 | if (res == true) { 47 | await library.sendKey('select'); 48 | res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'text', 'value': 'Please enter your username'}]}); 49 | if (res == false) { 50 | expect.fail("Can't enter user name"); 51 | } 52 | await library.sendWord('user'); 53 | await library.sendKeys(['down', 'down', 'down', 'down', 'select']); 54 | await library.sendWord('pass'); 55 | await library.sendKeys(['down', 'down', 'down', 'down', 'select']); 56 | } else { 57 | await library.sendKey('select'); 58 | } 59 | res = await library.verifyIsPlaybackStarted(); 60 | expect(res).to.equal(true); 61 | }); 62 | 63 | after(async () => { 64 | await library.close(); 65 | childProcess.kill(); 66 | }); 67 | }); 68 | 69 | -------------------------------------------------------------------------------- /jsLibrary/tests/test_video-preloading.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const rokuLibrary = require("../library/rokuLibrary"); 17 | const expect = require("chai").expect; 18 | const { spawn } = require('child_process'); 19 | 20 | const childProcess = spawn('D:/projects/go/webDriver/src/main.exe'); 21 | 22 | let library; 23 | 24 | describe('test_video-preloading', () => { 25 | before(() => { 26 | library = new rokuLibrary.Library("192.168.2.11"); 27 | }); 28 | 29 | it('should launch the channel', async function() { 30 | this.timeout(25000); 31 | await library.sideLoad("../channels/video_preloading.zip", "rokudev", "aaaa"); 32 | await library.verifyIsChannelLoaded('dev'); 33 | }); 34 | 35 | it('Verify is initial screen loaded', async function() { 36 | this.timeout(10000); 37 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'GridView'}]}); 38 | expect(res).to.equal(true); 39 | }); 40 | 41 | it('Verify is details screen loaded', async function() { 42 | this.timeout(50000); 43 | await library.sendKey('select', 3); 44 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'DetailsView'}]}); 45 | expect(res).equal(true); 46 | }); 47 | 48 | it('Verify is playback started', async function() { 49 | this.timeout(50000); 50 | await library.sendKey('select', 3); 51 | const res = await library.verifyIsPlaybackStarted(); 52 | expect(res).equal(true); 53 | }); 54 | 55 | it('Verify endcard after first video', async function() { 56 | this.timeout(80000); 57 | const res = await library.verifyIsScreenLoaded({'elementData': [{'using': 'tag', 'value': 'EndcardView'}]}, 15, 5); 58 | expect(res).equal(true); 59 | }); 60 | 61 | 62 | it('Verify is playback started after 10 seconds', async function() { 63 | this.timeout(50000); 64 | const res = await library.verifyIsPlaybackStarted(3, 5); 65 | expect(res).equal(true); 66 | }); 67 | 68 | after(async () => { 69 | await library.close(); 70 | childProcess.kill(); 71 | }); 72 | }); 73 | 74 | -------------------------------------------------------------------------------- /jsLibrary/uTest/responses.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const uiElement = { 17 | "XMLName": { 18 | "Space": "", 19 | "Local": "Label" 20 | }, 21 | "Attrs": [ 22 | { 23 | "Name": { 24 | "Space": "", 25 | "Local": "bounds" 26 | }, 27 | "Value": "{1, 1, 1, 1}" 28 | }, 29 | { 30 | "Name": { 31 | "Space": "", 32 | "Local": "color" 33 | }, 34 | "Value": "#ffffff6f" 35 | }, 36 | { 37 | "Name": { 38 | "Space": "", 39 | "Local": "index" 40 | }, 41 | "Value": "0" 42 | }, 43 | { 44 | "Name": { 45 | "Space": "", 46 | "Local": "text" 47 | }, 48 | "Value": "text" 49 | } 50 | ], 51 | "Nodes": null 52 | } 53 | 54 | module.exports = { 55 | uiElement: uiElement, 56 | respWithEmptyValue: { 57 | "sessionId": "test", 58 | "status": 0, 59 | "value": null 60 | }, 61 | requestLaunch: { 62 | 'channelId': /.+/i, 63 | 'contentId': '', 64 | 'contentType': '' 65 | }, 66 | requestInstall: { 67 | 'channelId': /.+/i 68 | }, 69 | resp: { 70 | 'sessionId': '11111', 71 | 'status': 0, 72 | 'value': { 73 | 'ip': '127.0.0.1', 74 | 'timeout': 20000, 75 | 'pressDelay': 2000 76 | } 77 | }, 78 | requestPress: { 79 | 'button': /.+/i 80 | }, 81 | requestSequence: { 82 | 'button_sequence': [/.+/i, /.+/i] 83 | }, 84 | respWithDeviceInfo: { 85 | "sessionId": "11111", 86 | "status": 0, 87 | "value": { 88 | "ip": "1.1.0.127", 89 | "vendorName": "Roku", 90 | "modelName": "Roku", 91 | "language": "en", 92 | "country": "US" 93 | } 94 | }, 95 | respWithApps: { 96 | "sessionId": "11111", 97 | "status": 0, 98 | "value": [{ 99 | "Title": "TestChannel", 100 | "ID": "id", 101 | "Type": "menu", 102 | "Version": "1.0.0", 103 | "Subtype": "" 104 | 105 | }] 106 | }, 107 | respWithApp: { 108 | "sessionId": "11111", 109 | "status": 0, 110 | "value": { 111 | "Title": "TestChannel", 112 | "ID": "id", 113 | "Type": "menu", 114 | "Version": "1.0.0", 115 | "Subtype": "" 116 | 117 | } 118 | }, 119 | respWithPlayerInfo: { 120 | "sessionId": "11111", 121 | "status": 0, 122 | "value": { 123 | "Error": "false", 124 | "State": "play", 125 | "Format": { 126 | "Audio": "", 127 | "Captions": "", 128 | "Container": "", 129 | "Drm": "", 130 | "Video": "", 131 | "VideoRes": "" 132 | }, 133 | "Buffering": { 134 | "Current": "", 135 | "Max": "", 136 | "Target": "" 137 | }, 138 | "NewStream": { 139 | "Speed": "" 140 | }, 141 | "Position": "1000 ms", 142 | "Duration": "20000 ms", 143 | "IsLive": "", 144 | "Runtime": "", 145 | "StreamSegment": { 146 | "Bitrate": "", 147 | "MediaSequence": "", 148 | "SegmentType": "", 149 | "Time": "" 150 | } 151 | } 152 | }, 153 | respWithElement: { 154 | "sessionId": "11111", 155 | "status": 0, 156 | "value": uiElement 157 | }, 158 | respWithElements: { 159 | "sessionId": "11111", 160 | "status": 0, 161 | "value":[uiElement] 162 | }, 163 | requestElement: { 164 | "elementData" :[{ 165 | "using": /.+/i, 166 | "value": /.+/i 167 | }] 168 | }, 169 | requestTimeout: { 170 | 'type': /.+/i, 171 | 'ms': /.+/i 172 | }, 173 | respWithSource: { 174 | "sessionId": "11111", 175 | "status": 0, 176 | "value": "dfverv5vrevrf==" 177 | }, 178 | responseWithError: { 179 | "sessionId": "11111", 180 | "status": 8, 181 | "value": { 182 | "message": "Error" 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /jsLibrary/uTest/test_client.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2020 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | const ecp = require("../library/client"); 17 | const expect = require("chai").expect; 18 | const nock = require('nock'); 19 | const { 20 | respWithEmptyValue, 21 | requestLaunch, 22 | resp, 23 | requestPress, 24 | requestInstall, 25 | requestSequence, 26 | respWithDeviceInfo, 27 | respWithApps, 28 | respWithApp, 29 | respWithPlayerInfo, 30 | respWithElement, 31 | requestElement, 32 | respWithElements, 33 | requestTimeout, 34 | respWithSource 35 | } = require('./responses') 36 | let client; 37 | const baseUrl = 'http://localhost:9000/v1/session'; 38 | const sessionId = resp.sessionId; 39 | 40 | describe('client tests', () => { 41 | before(async () => { 42 | nock('http://localhost:9000/v1') 43 | .post('/session', {'ip' : '127.0.0.1', 'timeout': 20000, 'pressDelay': 2000}) 44 | .reply(200, resp); 45 | client = new ecp.Client('127.0.0.1', 20000, 2000); 46 | }); 47 | 48 | it('test launch', async () => { 49 | nock(baseUrl) 50 | .post(`/${sessionId}/launch`, requestLaunch) 51 | .reply(200, respWithEmptyValue); 52 | const response = await client.launch('dev', '', ''); 53 | expect(JSON.stringify(respWithEmptyValue)).to.equal(JSON.stringify(response.data)); 54 | }); 55 | 56 | it('test sendKeypress', async () => { 57 | nock(baseUrl) 58 | .post(`/${sessionId}/press`, requestPress) 59 | .reply(200, respWithEmptyValue); 60 | const response = await client.sendKeypress('select'); 61 | expect(JSON.stringify(respWithEmptyValue)).to.equal(JSON.stringify(response.data)); 62 | }); 63 | 64 | it('test install', async () => { 65 | nock(baseUrl) 66 | .post(`/${sessionId}/install`, requestInstall) 67 | .reply(200, respWithEmptyValue); 68 | const response = await client.sendInstallChannel('1111'); 69 | expect(JSON.stringify(respWithEmptyValue)).to.equal(JSON.stringify(response.data)); 70 | }); 71 | 72 | it('test send sequence', async () => { 73 | nock(baseUrl) 74 | .post(`/${sessionId}/press`, requestSequence) 75 | .reply(200, respWithEmptyValue); 76 | const response = await client.sendSequence(['up', 'select']); 77 | expect(JSON.stringify(respWithEmptyValue)).to.equal(JSON.stringify(response.data)); 78 | }); 79 | 80 | it('test get device info', async () => { 81 | nock(baseUrl) 82 | .get(`/${sessionId}`) 83 | .reply(200, respWithDeviceInfo); 84 | const response = await client.getDeviceInfo(); 85 | expect(JSON.stringify(respWithDeviceInfo)).to.equal(JSON.stringify(response.data)); 86 | }); 87 | 88 | it('test get apps', async () => { 89 | nock(baseUrl) 90 | .get(`/${sessionId}/apps`) 91 | .reply(200, respWithApps); 92 | const response = await client.getApps(); 93 | expect(JSON.stringify(respWithApps)).to.equal(JSON.stringify(response.data)); 94 | }); 95 | 96 | it('test get current app', async () => { 97 | nock(baseUrl) 98 | .get(`/${sessionId}/current_app`) 99 | .reply(200, respWithApp); 100 | const response = await client.getCurrentApp(); 101 | expect(JSON.stringify(respWithApp)).to.equal(JSON.stringify(response.data)); 102 | }); 103 | 104 | it('test get player', async () => { 105 | nock(baseUrl) 106 | .get(`/${sessionId}/player`) 107 | .reply(200, respWithPlayerInfo); 108 | const response = await client.getPlayerInfo(); 109 | expect(JSON.stringify(respWithPlayerInfo)).to.equal(JSON.stringify(response.data)); 110 | }); 111 | 112 | it('test get ui element', async () => { 113 | nock(baseUrl) 114 | .post(`/${sessionId}/element`,requestElement) 115 | .reply(200, respWithElement); 116 | const response = await client.getUiElement({ 117 | "elementData" :[{ 118 | "using": "tag", 119 | "value": "Label" 120 | }] 121 | }); 122 | expect(JSON.stringify(respWithElement)).to.equal(JSON.stringify(response.data)); 123 | }); 124 | 125 | it('test get ui elements', async () => { 126 | nock(baseUrl) 127 | .post(`/${sessionId}/elements`, requestElement) 128 | .reply(200, respWithElements); 129 | const response = await client.getUiElements({ 130 | "elementData" :[{ 131 | "using": "tag", 132 | "value": "Label" 133 | }] 134 | }); 135 | expect(JSON.stringify(respWithElements)).to.equal(JSON.stringify(response.data)); 136 | }); 137 | 138 | it('test get active element', async () => { 139 | nock(baseUrl) 140 | .post(`/${sessionId}/element/active`, {}) 141 | .reply(200, respWithElement); 142 | const response = await client.getActiveElement(); 143 | expect(JSON.stringify(respWithElement)).to.equal(JSON.stringify(response.data)); 144 | }); 145 | 146 | it('test set timeouts', async () => { 147 | nock(baseUrl) 148 | .post(`/${sessionId}/timeouts`, requestTimeout) 149 | .reply(200, respWithEmptyValue); 150 | const response = await client.setTimeouts("implicit", 1000); 151 | expect(JSON.stringify(respWithEmptyValue)).to.equal(JSON.stringify(response.data)); 152 | }); 153 | 154 | it('test get screen source', async () => { 155 | nock(baseUrl) 156 | .get(`/${sessionId}/source`) 157 | .reply(200, respWithSource); 158 | const response = await client.getScreenSource(); 159 | expect(JSON.stringify(respWithSource)).to.equal(JSON.stringify(response.data)); 160 | }); 161 | 162 | it('test send input data', async () => { 163 | nock(baseUrl) 164 | .post(`/${sessionId}/input`) 165 | .reply(200, respWithEmptyValue); 166 | const response = await client.sendInputData('dev', '12', 'movie'); 167 | expect(JSON.stringify(respWithEmptyValue)).to.equal(JSON.stringify(response.data)); 168 | }); 169 | 170 | it('test deleteSession', async () => { 171 | nock(baseUrl) 172 | .delete(`/${sessionId}`) 173 | .reply(200, respWithEmptyValue); 174 | const response = await client.deleteSession(); 175 | expect(JSON.stringify(respWithEmptyValue)).to.equal(JSON.stringify(response.data)); 176 | }); 177 | }); 178 | 179 | -------------------------------------------------------------------------------- /sample/Postman/README.txt: -------------------------------------------------------------------------------- 1 | WebDriver Postman collection 2 | 3 | 1.) Side load Roku Recommends sample channel. 4 | 5 | 2.) WebDriver_endpoints file should be imported as Postman collection. 6 | 7 | 3.) Open "create session" request and populate "ip" field in request body with your Roku Device IP adress. 8 | 9 | 4.) Start instance of Roku webDriver. 10 | 11 | 5.) Execute "create session" request to create new session. 12 | 13 | 6.) Exucute all other request. 14 | 15 | 7.) Execute "delete session" request to remove session. 16 | 17 | 18 | -------------------------------------------------------------------------------- /sample/channel.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rokudev/automated-channel-testing/13528aefbda05ced9895ec24e9e258d02131ec82/sample/channel.zip -------------------------------------------------------------------------------- /sample/script/main.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | from webDriver import WebDriver 18 | import sys 19 | 20 | def run(ip: str): 21 | try: 22 | web_driver = WebDriver(ip) 23 | web_driver.launch_the_channel("dev") 24 | web_driver.verify_is_screen_loaded({"elementData": [{ 25 | "using": "text", 26 | "value": "ROW 1" 27 | }]}) 28 | 29 | web_driver.press_btn("select") 30 | web_driver.verify_is_screen_loaded({"elementData": [{ 31 | "using": "text", 32 | "value": "Barack Gates, Bill Obama" 33 | }]}) 34 | 35 | res = web_driver.verify_is_screen_loaded({"elementData": [{ 36 | "using": "text", 37 | "value": "Authenticate to watch" 38 | }]}, False, 2) 39 | if res == False: 40 | res = web_driver.verify_is_screen_loaded({"elementData": [{ 41 | "using": "text", 42 | "value": "Play" 43 | }]}) 44 | web_driver.press_btn("select") 45 | else: 46 | web_driver.press_btn("select") 47 | web_driver.verify_is_screen_loaded({"elementData": [{ 48 | "using": "text", 49 | "value": "Please enter your username" 50 | }]}) 51 | web_driver.send_word("user") 52 | web_driver.send_button_sequence(["down", "down", "down", "down", "select"]) 53 | web_driver.verify_is_screen_loaded({"elementData": [{ 54 | "using": "text", 55 | "value": "Please enter your password" 56 | }]}) 57 | web_driver.send_word("pass") 58 | web_driver.send_button_sequence(["down", "down", "down", "down", "select"]) 59 | print(f"{ip}: Test passed") 60 | except Exception as e: 61 | print(f"{ip}: Error: {e}") 62 | print(f"{ip}: Test failed") 63 | finally: 64 | web_driver.quiet() 65 | 66 | if __name__ == "__main__": 67 | run(sys.argv[1]) -------------------------------------------------------------------------------- /sample/script/multipleDeviceSample.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | from main import run 18 | import sys 19 | import threading 20 | 21 | if __name__ == "__main__": 22 | arguments_list = sys.argv[1:] 23 | for ip in arguments_list: 24 | thread = threading.Thread(target=run, args=(ip,)) 25 | thread.start() 26 | -------------------------------------------------------------------------------- /sample/script/webDriver.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright 2019 Roku, Inc. 3 | # 4 | #Licensed under the Apache License, Version 2.0 (the "License"); 5 | #you may not use this file except in compliance with the License. 6 | #You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | #Unless required by applicable law or agreed to in writing, software 11 | #distributed under the License is distributed on an "AS IS" BASIS, 12 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | #See the License for the specific language governing permissions and 14 | #limitations under the License. 15 | ######################################################################## 16 | 17 | import requests 18 | import json 19 | from time import sleep 20 | 21 | class WebDriver: 22 | def __init__(self, roku_ip_address: str): 23 | data = {'ip' : roku_ip_address} 24 | request_url = self._build_request_url('') 25 | response = self._post(request_url, data) 26 | res = json.loads(response.text) 27 | self._session_id = res['sessionId'] 28 | 29 | def _send_launch_channel(self, channel_code: str): 30 | data = {'channelId' : channel_code} 31 | request_url = self._build_request_url(f"/{self._session_id}/launch") 32 | return self._post(request_url, data) 33 | 34 | def _send_sequence(self, sequence): 35 | data = {'button_sequence' : sequence} 36 | request_url = self._build_request_url(f"/{self._session_id}/press") 37 | return self._post(request_url, data) 38 | 39 | def _get_ui_element(self, data: object): 40 | request_url = self._build_request_url(f"/{self._session_id}/element") 41 | return self._post(request_url, data) 42 | 43 | def _send_keypress(self, key_press: str): 44 | data = {'button' : key_press} 45 | request_url = self._build_request_url(f"/{self._session_id}/press") 46 | return self._post(request_url, data) 47 | 48 | def _build_request_url(self, endpoint: str): 49 | return f"http://localhost:9000/v1/session{endpoint}" 50 | 51 | def quiet(self): 52 | request_url = self._build_request_url(f"/{self._session_id}") 53 | self._delete(request_url) 54 | 55 | def _post(self, request_url: str, data: object): 56 | return requests.post(url = request_url, data = json.dumps(data)) 57 | 58 | def _get(self, request_url: str): 59 | return requests.get(request_url) 60 | 61 | def _delete(self, request_url: str): 62 | return requests.delete(request_url) 63 | 64 | def launch_the_channel(self, channel_code): 65 | launch_response = self._send_launch_channel(channel_code) 66 | if launch_response.status_code != 200: 67 | raise Exception("Wrong launch response code") 68 | 69 | def verify_is_screen_loaded(self, data: object, invoke_error = True, retries = 10): 70 | while retries > 0: 71 | ui_layout_response = self._get_ui_element(data) 72 | if ui_layout_response.status_code != 200: 73 | retries -= 1 74 | sleep(1) 75 | else: 76 | return True 77 | if invoke_error == True: 78 | raise Exception("Can't find element") 79 | else: 80 | return False 81 | 82 | def press_btn(self, key_press: str): 83 | sleep(2) 84 | key_press_response = self._send_keypress(key_press) 85 | if key_press_response.status_code != 200: 86 | raise Exception("Wrong keypress response code") 87 | 88 | def send_word(self, word: str): 89 | sleep(2) 90 | for c in word: 91 | key_press_response = self._send_keypress(f"LIT_{c}") 92 | if key_press_response.status_code != 200: 93 | raise Exception("Wrong keypress response code") 94 | 95 | def send_button_sequence(self, sequence): 96 | key_press_response = self._send_sequence(sequence) 97 | if key_press_response.status_code != 200: 98 | raise Exception("Wrong keypress response code") -------------------------------------------------------------------------------- /src/ecpClient/ecp_parser.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package ecpClient 17 | 18 | import ( 19 | "net/http" 20 | "encoding/xml" 21 | "io/ioutil" 22 | "image/png" 23 | "image" 24 | ) 25 | 26 | func (ec *EcpClient) parseApps(res *http.Response) (*[]App, error) { 27 | var apps AppsResponse 28 | result, err := ec.parseResponse(res, &apps) 29 | if err != nil { 30 | return nil, err 31 | } 32 | parsedRes := result.(*AppsResponse) 33 | return &parsedRes.Apps, err 34 | } 35 | 36 | func (ec *EcpClient) parsePlayer(res *http.Response) (*Player, error) { 37 | var player Player 38 | result, err := ec.parseResponse(res, &player) 39 | if err != nil { 40 | return nil, err 41 | } 42 | parsedRes := result.(*Player) 43 | return parsedRes, err 44 | } 45 | 46 | func (ec *EcpClient) parseActiveApp(res *http.Response) (*App, error) { 47 | var app ActiveAppResponse 48 | result, err := ec.parseResponse(res, &app) 49 | if err != nil { 50 | return nil, err 51 | } 52 | parsedRes := result.(*ActiveAppResponse) 53 | return &parsedRes.ActiveApp, err 54 | } 55 | 56 | func (ec *EcpClient) parseDeviceInfo(res *http.Response) (*DeviceInfo, error) { 57 | var responseStructure DeviceInfo 58 | result, err := ec.parseResponse(res, &responseStructure) 59 | if err != nil { 60 | return nil, err 61 | } 62 | parsedRes := result.(*DeviceInfo) 63 | return parsedRes, err 64 | } 65 | 66 | func (ec *EcpClient) parseAppUi(res *http.Response) (*Node, error) { 67 | var responseStructure Node 68 | result, err := ec.parseResponse(res, &responseStructure) 69 | if err != nil { 70 | return nil, err 71 | } 72 | parsedRes := result.(*Node) 73 | return parsedRes, err 74 | } 75 | 76 | func (ec *EcpClient) parseImage(res *http.Response) (image.Image, error) { 77 | result, err := png.Decode(res.Body) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return result, err 82 | } 83 | 84 | 85 | func (ec *EcpClient) parseAppUiSource(res *http.Response) ([]byte, error) { 86 | data, err := ioutil.ReadAll(res.Body) 87 | if err != nil { 88 | return nil, err 89 | } 90 | return data, nil 91 | } 92 | 93 | func (ec *EcpClient) parseResponse(res *http.Response, v interface{}) (interface{}, error) { 94 | data, err := ioutil.ReadAll(res.Body) 95 | if err != nil { 96 | return nil, err 97 | } 98 | err = xml.Unmarshal(data, v) 99 | if err != nil { 100 | return nil, err 101 | } 102 | return v, err 103 | } -------------------------------------------------------------------------------- /src/ecpClient/http_client.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package ecpClient 17 | 18 | import ( 19 | "net/http" 20 | "time" 21 | ) 22 | 23 | type Option func(*Client) 24 | 25 | func SetHTTPClient(httpClient *http.Client) Option { 26 | return func(cli *Client) { 27 | cli.HttpClient = httpClient 28 | } 29 | } 30 | 31 | func SetRequestTimeout(timeout time.Duration) Option { 32 | return func(cli *Client) { 33 | cli.HttpClient.Timeout = timeout 34 | } 35 | } 36 | 37 | type Client struct { 38 | HttpClient *http.Client 39 | } 40 | 41 | func NewClient(options ...Option) *Client { 42 | cli := Client{ 43 | HttpClient: &http.Client{}, 44 | } 45 | 46 | for i := range options { 47 | options[i](&cli) 48 | } 49 | 50 | return &cli 51 | } 52 | -------------------------------------------------------------------------------- /src/ecpClient/http_client_mock.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package ecpClient 17 | 18 | import ( 19 | "bytes" 20 | "io/ioutil" 21 | "net/http" 22 | "net/url" 23 | ) 24 | 25 | var base, _ = url.Parse("some.api") 26 | // RoundTripFunc . 27 | type RoundTripFunc func(req *http.Request) (*http.Response, error) 28 | 29 | // RoundTrip . 30 | func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { 31 | return f(req) 32 | } 33 | 34 | //NewTestClient returns *http.Client with Transport replaced to avoid making real calls 35 | func NewTestClient(fn RoundTripFunc) *http.Client { 36 | return &http.Client{ 37 | Transport: RoundTripFunc(fn), 38 | } 39 | } 40 | 41 | func GetMockedClient(resp *string) *EcpClient { 42 | httpClient := TestingHTTPClient(resp) 43 | 44 | cli := NewClient() 45 | cli.HttpClient = httpClient 46 | 47 | contentsClient := EcpClient{ 48 | BaseClient: &BaseClient{ 49 | BaseURL: base, 50 | HttpClient: cli, 51 | }, 52 | } 53 | 54 | return &contentsClient 55 | } 56 | 57 | 58 | func TestingHTTPClient(resp *string) *http.Client { 59 | var function func(r *http.Request) (*http.Response, error) 60 | if resp != nil{ 61 | function = func(req *http.Request) (*http.Response, error) { 62 | // Test request parameters 63 | return &http.Response{ 64 | StatusCode: 200, 65 | // Send response to be tested 66 | Body: ioutil.NopCloser(bytes.NewBufferString(*resp)), 67 | // Must be set to non-nil value or it panics 68 | Header: make(http.Header), 69 | }, nil 70 | } 71 | } else { 72 | function = func(req *http.Request) (*http.Response, error) { 73 | return &http.Response{ 74 | StatusCode: 408, 75 | // Send response to be tested 76 | Body: nil, 77 | // Must be set to non-nil value or it panics 78 | Header: make(http.Header), 79 | }, http.ErrHandlerTimeout 80 | } 81 | } 82 | 83 | cli := NewTestClient(function) 84 | return cli 85 | } 86 | -------------------------------------------------------------------------------- /src/ecpClient/response_mocks.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package ecpClient 17 | 18 | var SuccessResponseMock = `{}` 19 | 20 | var AppResponseMock =` 21 | 22 | test 23 | ` 24 | 25 | var AppsResponseMock = ` 26 | 27 | test 28 | test2 29 | ` 30 | 31 | var deviceInfoResponse =` 32 | 33 | test 34 | test 35 | test 36 | test 37 | test 38 | test 39 | test 40 | test 41 | test 42 | test 43 | test 44 | test 45 | test 46 | test 47 | test 48 | test 49 | test 50 | test 51 | test 52 | test 53 | test 54 | test 55 | test 56 | test 57 | test 58 | test 59 | test 60 | test 61 | test 62 | test 63 | test 64 | test 65 | test 66 | test 67 | test 68 | test 69 | test 70 | test 71 | test 72 | test 73 | test 74 | test 75 | test 76 | test 77 | test 78 | test 79 | test 80 | test 81 | test 82 | test 83 | test 84 | test 85 | test 86 | test 87 | test 88 | test 89 | test 90 | test 91 | test 92 | test 93 | ` 94 | 95 | var UiResponseMock = ` 96 | 97 | 98 | 99 | ` -------------------------------------------------------------------------------- /src/ecpClient/response_structure.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package ecpClient 17 | 18 | import ( 19 | "encoding/xml" 20 | ) 21 | type AppsResponse struct { 22 | Apps []App `xml:"app"` 23 | } 24 | 25 | type App struct { 26 | Title string `xml:",chardata"` 27 | ID string `xml:"id,attr"` 28 | Type string `xml:"type,attr"` 29 | Version string `xml:"version,attr"` 30 | Subtype string `xml:"subtype,attr"` 31 | } 32 | 33 | type Player struct { 34 | Error string `xml:"error,attr"` 35 | State string `xml:"state,attr"` 36 | Format struct { 37 | Audio string `xml:"audio,attr"` 38 | Captions string `xml:"captions,attr"` 39 | Container string `xml:"container,attr"` 40 | Drm string `xml:"drm,attr"` 41 | Video string `xml:"video,attr"` 42 | VideoRes string `xml:"video_res,attr"` 43 | } `xml:"format"` 44 | Buffering struct { 45 | Current string `xml:"current,attr"` 46 | Max string `xml:"max,attr"` 47 | Target string `xml:"target,attr"` 48 | } `xml:"buffering"` 49 | NewStream struct { 50 | Speed string `xml:"speed,attr"` 51 | } `xml:"new_stream"` 52 | Position string `xml:"position"` 53 | Duration string `xml:"duration"` 54 | IsLive string `xml:"is_live"` 55 | Runtime string `xml:"runtime"` 56 | StreamSegment struct { 57 | Bitrate string `xml:"bitrate,attr"` 58 | MediaSequence string `xml:"media_sequence,attr"` 59 | SegmentType string `xml:"segment_type,attr"` 60 | Time string `xml:"time,attr"` 61 | } `xml:"stream_segment"` 62 | } 63 | 64 | type ActiveAppResponse struct { 65 | ActiveApp App `xml:"app"` 66 | } 67 | 68 | type Node struct { 69 | XMLName xml.Name 70 | Attrs []xml.Attr `xml:",any,attr"` 71 | Nodes []Node `xml:",any"` 72 | } 73 | 74 | type DeviceInfo struct { 75 | Udn string `xml:"udn"` 76 | SerialNumber string `xml:"serial-number"` 77 | DeviceID string `xml:"device-id"` 78 | AdvertisingID string `xml:"advertising-id"` 79 | VendorName string `xml:"vendor-name"` 80 | ModelName string `xml:"model-name"` 81 | ModelNumber string `xml:"model-number"` 82 | ModelRegion string `xml:"model-region"` 83 | IsTv string `xml:"is-tv"` 84 | IsStick string `xml:"is-stick"` 85 | SupportsEthernet string `xml:"supports-ethernet"` 86 | WifiMac string `xml:"wifi-mac"` 87 | WifiDriver string `xml:"wifi-driver"` 88 | EthernetMac string `xml:"ethernet-mac"` 89 | NetworkType string `xml:"network-type"` 90 | NetworkName string `xml:"network-name"` 91 | FriendlyDeviceName string `xml:"friendly-device-name"` 92 | FriendlyModelName string `xml:"friendly-model-name"` 93 | DefaultDeviceName string `xml:"default-device-name"` 94 | UserDeviceName string `xml:"user-device-name"` 95 | UserDeviceLocation string `xml:"user-device-location"` 96 | BuildNumber string `xml:"build-number"` 97 | SoftwareVersion string `xml:"software-version"` 98 | SoftwareBuild string `xml:"software-build"` 99 | SecureDevice string `xml:"secure-device"` 100 | Language string `xml:"language"` 101 | Country string `xml:"country"` 102 | Locale string `xml:"locale"` 103 | TimeZoneAuto string `xml:"time-zone-auto"` 104 | TimeZone string `xml:"time-zone"` 105 | TimeZoneName string `xml:"time-zone-name"` 106 | TimeZoneTz string `xml:"time-zone-tz"` 107 | TimeZoneOffset string `xml:"time-zone-offset"` 108 | ClockFormat string `xml:"clock-format"` 109 | Uptime string `xml:"uptime"` 110 | PowerMode string `xml:"power-mode"` 111 | SupportsSuspend string `xml:"supports-suspend"` 112 | SupportsFindRemote string `xml:"supports-find-remote"` 113 | FindRemoteIsPossible string `xml:"find-remote-is-possible"` 114 | SupportsAudioGuide string `xml:"supports-audio-guide"` 115 | SupportsRva string `xml:"supports-rva"` 116 | DeveloperEnabled string `xml:"developer-enabled"` 117 | KeyedDeveloperID string `xml:"keyed-developer-id"` 118 | SearchEnabled string `xml:"search-enabled"` 119 | SearchChannelsEnabled string `xml:"search-channels-enabled"` 120 | NotificationsEnabled string `xml:"notifications-enabled"` 121 | NotificationsFirstUse string `xml:"notifications-first-use"` 122 | SupportsPrivateListening string `xml:"supports-private-listening"` 123 | HeadphonesConnected string `xml:"headphones-connected"` 124 | SupportsEcsTextedit string `xml:"supports-ecs-textedit"` 125 | SupportsEcsMicrophone string `xml:"supports-ecs-microphone"` 126 | SupportsWakeOnWlan string `xml:"supports-wake-on-wlan"` 127 | HasPlayOnRoku string `xml:"has-play-on-roku"` 128 | HasMobileScreensaver string `xml:"has-mobile-screensaver"` 129 | SupportURL string `xml:"support-url"` 130 | GrandcentralVersion string `xml:"grandcentral-version"` 131 | TrcVersion string `xml:"trc-version"` 132 | TrcChannelVersion string `xml:"trc-channel-version"` 133 | DavinciVersion string `xml:"davinci-version"` 134 | } 135 | -------------------------------------------------------------------------------- /src/httpServer/middleware.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package httpServer 17 | 18 | import( 19 | "net/http" 20 | "encoding/json" 21 | ) 22 | 23 | type errorResponse struct { 24 | Message string `json:"message"` 25 | } 26 | 27 | func Middleware(h appHandler) http.Handler { 28 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 29 | if e := h(w, r); e != nil { 30 | if e.InternalCode != nil { 31 | w.WriteHeader(e.Code) 32 | errorResponse := &errorResponse{ 33 | Message: e.Message, 34 | } 35 | response := &SessionResponse{ 36 | Status: *e.InternalCode, 37 | Value: errorResponse, 38 | } 39 | js, _ := json.Marshal(response) 40 | w.Header().Set("Content-Type", "application/json") 41 | w.Write(js) 42 | } else { 43 | http.Error(w, e.Message, e.Code) 44 | } 45 | 46 | } 47 | }) 48 | } -------------------------------------------------------------------------------- /src/httpServer/response_mock.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package httpServer 17 | 18 | var validAppsResponse = ` 19 | { 20 | "sessionId": "test", 21 | "status": 0, 22 | "value": [ 23 | { 24 | "Title": "test", 25 | "ID": "test", 26 | "Type": "test", 27 | "Version": "test", 28 | "Subtype": "test" 29 | }, 30 | { 31 | "Title": "test2", 32 | "ID": "test2", 33 | "Type": "test2", 34 | "Version": "test2", 35 | "Subtype": "test2" 36 | } 37 | ] 38 | }` 39 | 40 | var validAppResponse = ` 41 | { 42 | "sessionId": "test", 43 | "status": 0, 44 | "value": { 45 | "Title": "test", 46 | "ID": "test", 47 | "Type": "test", 48 | "Version": "test", 49 | "Subtype": "test" 50 | } 51 | }` 52 | 53 | var validResponseWithNullValue = ` 54 | { 55 | "sessionId": "test", 56 | "status": 0, 57 | "value": null 58 | }` 59 | 60 | var validSessionResponse = ` 61 | { 62 | "sessionId":"test", 63 | "status":0, 64 | "value": { 65 | "country":"", 66 | "ip":"", 67 | "language":"", 68 | "modelName":"", 69 | "vendorName":"", 70 | "pressDelay": 0, 71 | "timeout": 0 72 | } 73 | }` 74 | 75 | var validSessionsResponse = `[ 76 | { 77 | "sessionId":"test", 78 | "status":0, 79 | "value": { 80 | "country":"", 81 | "ip":"", 82 | "language":"", 83 | "modelName":"", 84 | "vendorName":"", 85 | "pressDelay": 0, 86 | "timeout": 0 87 | } 88 | }]` 89 | 90 | var validElementResponse = ` 91 | { 92 | "sessionId":"test", 93 | "status":0, 94 | "value": { 95 | "XMLName": { 96 | "Local": "Poster", 97 | "Space": "" 98 | }, 99 | "Attrs": [ 100 | { 101 | "Name": { 102 | "Local": "bounds", 103 | "Space": "" 104 | }, 105 | "Value": "{0, 0, 1920, 1080}" 106 | }, 107 | { 108 | "Name": { 109 | "Local": "index", 110 | "Space": "" 111 | }, 112 | "Value": "0" 113 | }, 114 | { 115 | "Name": { 116 | "Local": "loadStatus", 117 | "Space": "" 118 | }, 119 | "Value": "3" 120 | } 121 | ], 122 | "Nodes": null 123 | } 124 | }` 125 | 126 | var validElementsResponse = ` 127 | { 128 | "sessionId":"test", 129 | "status":0, 130 | "value": [{ 131 | "XMLName": { 132 | "Local": "Poster", 133 | "Space": "" 134 | }, 135 | "Attrs": [ 136 | { 137 | "Name": { 138 | "Local": "bounds", 139 | "Space": "" 140 | }, 141 | "Value": "{0, 0, 1920, 1080}" 142 | }, 143 | { 144 | "Name": { 145 | "Local": "index", 146 | "Space": "" 147 | }, 148 | "Value": "0" 149 | }, 150 | { 151 | "Name": { 152 | "Local": "loadStatus", 153 | "Space": "" 154 | }, 155 | "Value": "3" 156 | } 157 | ], 158 | "Nodes": null 159 | }] 160 | }` -------------------------------------------------------------------------------- /src/httpServer/response_structure.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package httpServer 17 | 18 | type BuildInfo struct { 19 | Version string `json:"version"` 20 | Time string `json:"time"` 21 | } 22 | 23 | type OsInfo struct { 24 | Arch string `json:"arch"` 25 | Name string `json:"name"` 26 | } 27 | 28 | type Status struct { 29 | Build BuildInfo `json:"build"` 30 | Os OsInfo `json:"os"` 31 | } 32 | 33 | type Capability struct { 34 | Ip string `json:"ip"` 35 | Timeout int `json:"timeout"` 36 | PressDelay int `json:"pressDelay"` 37 | VendorName string `json:"vendorName"` 38 | ModelName string `json:"modelName"` 39 | Language string `json:"language"` 40 | Country string `json:"country"` 41 | } 42 | 43 | type SessionResponse struct { 44 | Id string `json:"sessionId"` 45 | Status int `json:"status"` 46 | Value interface{} `json:"value"` 47 | } 48 | 49 | var responseStatuses = map[string]int { 50 | "Success": 0, 51 | "NoSuchDriver": 6, 52 | "NoSuchElement": 7, 53 | "UnknownCommand": 9, 54 | "UnknownError": 13, 55 | "SessionNotCreatedException": 33, 56 | } 57 | 58 | type ButtonRequest struct { 59 | Button string `json:"button"` 60 | Button_sequence []string `json:"button_sequence"` 61 | Button_delays []string `json:"button_delays"` 62 | } 63 | 64 | type Element struct { 65 | Using string `json:"using"` 66 | Value string `json:"value"` 67 | Attribute string `json:"attribute"` 68 | } 69 | 70 | type ElementRequest struct { 71 | ParentData []Element `json:"parentData"` 72 | ElementData []Element `json:"elementData"` 73 | } 74 | 75 | type ChannelRequest struct { 76 | ChannelId string `json:"channelId"` 77 | ContentType string `json:"contentType"` 78 | ContentId string `json:"contentId"` 79 | } 80 | 81 | type TimeoutRequest struct { 82 | Type string `json:"type"` 83 | Ms int `json:"ms"` 84 | } 85 | 86 | type IntentRequest struct { 87 | Intent string `json:"intent"` 88 | } -------------------------------------------------------------------------------- /src/httpServer/routing.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package httpServer 17 | 18 | import( 19 | "net/http" 20 | ) 21 | 22 | type Route struct { 23 | Method string 24 | Pattern string 25 | HandlerFunc appHandler 26 | } 27 | 28 | type Routes []Route 29 | 30 | func (s *Server) SetUpRoutes() { 31 | routes := Routes{ 32 | Route{ 33 | "GET", 34 | "/v1/status", 35 | s.GetStatusHandler(), 36 | }, 37 | Route{ 38 | "POST", 39 | "/v1/session/{sessionId}/load", 40 | s.GetLoadHandler(), 41 | }, 42 | Route{ 43 | "POST", 44 | "/v1/session", 45 | s.GetStartSessionHandler(), 46 | }, 47 | Route{ 48 | "GET", 49 | "/v1/sessions", 50 | s.GetSessionsInfoHandler(), 51 | }, 52 | Route{ 53 | "GET", 54 | "/v1/session/{sessionId}", 55 | s.GetSessionHandler(), 56 | }, 57 | Route{ 58 | "DELETE", 59 | "/v1/session/{sessionId}", 60 | s.GetSessionDeleteHandler(), 61 | }, 62 | Route{ 63 | "POST", 64 | "/v1/session/{sessionId}/timeouts", 65 | s.GetTimeoutsHandler(), 66 | }, 67 | Route{ 68 | "POST", 69 | "/v1/session/{sessionId}/element", 70 | s.GetElementHandler(), 71 | }, 72 | Route{ 73 | "GET", 74 | "/v1/session/{sessionId}/player", 75 | s.GetPlayerHandler(), 76 | }, 77 | Route{ 78 | "POST", 79 | "/v1/session/{sessionId}/elements", 80 | s.GetElementsHandler(), 81 | }, 82 | Route{ 83 | "POST", 84 | "/v1/session/{sessionId}/element/active", 85 | s.GetActiveElementHandler(), 86 | }, 87 | Route{ 88 | "GET", 89 | "/v1/session/{sessionId}/apps", 90 | s.GetAppsHandler(), 91 | }, 92 | Route{ 93 | "GET", 94 | "/v1/session/{sessionId}/current_app", 95 | s.GetCurrentAppHandler(), 96 | }, 97 | Route{ 98 | "GET", 99 | "/v1/session/{sessionId}/source", 100 | s.GetSourceHandler(), 101 | }, 102 | Route{ 103 | "POST", 104 | "/v1/session/{sessionId}/press", 105 | s.GetPressButtonHandler(), 106 | }, 107 | Route{ 108 | "POST", 109 | "/v1/session/{sessionId}/launch", 110 | s.GetLaunchHandler(), 111 | }, 112 | Route{ 113 | "POST", 114 | "/v1/session/{sessionId}/input", 115 | s.GetInputHandler(), 116 | }, 117 | Route{ 118 | "POST", 119 | "/v1/session/{sessionId}/timeouts/implicit_wait", 120 | s.GetImplicitTimeoutHandler(), 121 | }, 122 | Route{ 123 | "POST", 124 | "/v1/session/{sessionId}/timeouts/press_wait", 125 | s.GetImplicitTimeoutHandler(), 126 | }, 127 | Route{ 128 | "POST", 129 | "/v1/session/{sessionId}/install", 130 | s.GetInstallHandler(), 131 | }, 132 | } 133 | 134 | 135 | for _, route := range routes { 136 | s.router. 137 | Methods(route.Method). 138 | Path(route.Pattern). 139 | Handler(Middleware(route.HandlerFunc)).GetError() 140 | } 141 | s.router.NotFoundHandler = http.Handler(Middleware(s.notFound())) 142 | http.Handle("/", s.router) 143 | } -------------------------------------------------------------------------------- /src/httpServer/server.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package httpServer 17 | 18 | import ( 19 | "github.com/gorilla/mux" 20 | ecp "ecpClient" 21 | "net/http" 22 | "time" 23 | "github.com/sirupsen/logrus" 24 | ) 25 | 26 | 27 | 28 | type Server struct { 29 | router *mux.Router 30 | sessions map[string]*SessionInfo 31 | } 32 | 33 | type SessionInfo struct { 34 | client *ecp.EcpClient 35 | plugin *ecp.PluginClient 36 | capability *Capability 37 | pressDelay time.Duration 38 | } 39 | 40 | func GetServerInstance() *Server { 41 | server := &Server{ 42 | router: mux.NewRouter(), 43 | sessions: make(map[string]*SessionInfo), 44 | } 45 | 46 | return server 47 | } 48 | 49 | func (s *Server) Start(port string) { 50 | s.SetUpRoutes() 51 | err := http.ListenAndServe(":" + port, nil) 52 | if err != http.ErrServerClosed { 53 | logrus.WithError(err).Error("Http Server stopped unexpected") 54 | } else { 55 | logrus.WithError(err).Info("Http Server stopped") 56 | } 57 | } -------------------------------------------------------------------------------- /src/httpServer/utils.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package httpServer 17 | 18 | import ( 19 | "regexp" 20 | "strings" 21 | ) 22 | 23 | func validIP4(ipAddress string) bool { 24 | ipAddress = strings.Trim(ipAddress, " ") 25 | 26 | re, _ := regexp.Compile(`^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`) 27 | if re.MatchString(ipAddress) { 28 | return true 29 | } 30 | return false 31 | } -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | httpServer "httpServer" 21 | "os" 22 | "regexp" 23 | ) 24 | 25 | func main() { 26 | defaultPort := "9000" 27 | validPort := regexp.MustCompile(`^[0-9]+$`) 28 | server := httpServer.GetServerInstance() 29 | if len(os.Args) > 1 && validPort.MatchString(os.Args[1]) { 30 | fmt.Println("Starting driver on port: " + os.Args[1]) 31 | server.Start(os.Args[1]) 32 | } else { 33 | fmt.Println("Starting driver on port: " + defaultPort) 34 | server.Start(defaultPort) 35 | } 36 | } -------------------------------------------------------------------------------- /src/version/version.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 Roku, Inc. 3 | // 4 | //Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | //You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | //Unless required by applicable law or agreed to in writing, software 10 | //distributed under the License is distributed on an "AS IS" BASIS, 11 | //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | //See the License for the specific language governing permissions and 13 | //limitations under the License. 14 | ////////////////////////////////////////////////////////////////////////// 15 | 16 | package version 17 | 18 | var ( 19 | BuildVersion string = "" 20 | BuildTime string = "" 21 | ) --------------------------------------------------------------------------------