Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fully automated Testing Façade for NMOS Controller testing #82

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c757669
Initial add of automated Testing Facade
hrbriggs Jul 30, 2021
3998dbf
Added names to components required in automated testing
hrbriggs Aug 10, 2021
f091d9e
Names for active and staged tabs
hrbriggs Aug 13, 2021
aa128f0
Updated automated tests to find elements using names. Added test_07, …
hrbriggs Aug 13, 2021
8df98e4
Updated automated tests to use implicit wait to prevent obscured elem…
hrbriggs Aug 17, 2021
59eb485
Fixed bugs in setup preventing tests from running without user interv…
hrbriggs Aug 23, 2021
689b9dd
Use question id to trigger automated tests rather than test name
hrbriggs Aug 23, 2021
3e1a54b
Updated automated tests to find and return answer ids rather than lab…
hrbriggs Aug 25, 2021
50bbc7f
Names for pagination buttons
hrbriggs Sep 8, 2021
a765dd6
Updated Automated test_03 and 04 to work with pagination
hrbriggs Sep 8, 2021
f037056
Extra sleep on test_09 to make sure updated receiver list has loaded
hrbriggs Sep 13, 2021
7a97d66
Removed unnecessary methods from DataStore. Added new method for mini…
hrbriggs Sep 13, 2021
6a9749f
Automated tests now run in headless Chrome browser mode, with a fresh…
hrbriggs Sep 24, 2021
43e8cdc
Added extra sleeps after refresh button clicks to ensure new data has…
hrbriggs Sep 30, 2021
92fd106
Message to indicate end of tests
hrbriggs Sep 30, 2021
6a218c6
Split test suites to match new nmos testing versions. Specify suite o…
hrbriggs Dec 14, 2021
566112c
Updates to match changes to nmos-testing
hrbriggs Jan 11, 2022
22b59a1
Fix linting issues, ignoring line length
hrbriggs Jan 13, 2022
5d9ae10
Updates to keep up with controller testing changes
hrbriggs Jan 18, 2022
bd549a4
Update README
hrbriggs Jan 18, 2022
4c9b86d
Added warnings for missing or invalid command line arguments
hrbriggs Jan 19, 2022
61b59b3
Added config file
hrbriggs Jan 20, 2022
9a19ffb
Set browser in config
hrbriggs Jan 20, 2022
0e3fd0e
Removed unused functions
hrbriggs Feb 4, 2022
c0d2194
Updated comments and change to pagination tests
hrbriggs Feb 15, 2022
e754204
Linting errors and config file for flake8 matching nmos-testing
hrbriggs Feb 15, 2022
95adf7a
Linting errors
hrbriggs Feb 16, 2022
3e32438
Tidied up TestingFacade
hrbriggs Feb 16, 2022
5c55426
Removed redundant functions
hrbriggs Feb 17, 2022
940d106
Added port to config
hrbriggs Feb 17, 2022
3febc7d
Updated README
hrbriggs Feb 17, 2022
a162d65
The Connect tab should allow a Receiver that supports only either uni…
garethsb Nov 22, 2021
5a6c451
data -> dataStore
hrbriggs Feb 21, 2022
99c4f1a
Better variable names and error handling
hrbriggs Feb 21, 2022
c6dffe8
Added Generic base class
hrbriggs Feb 21, 2022
106c530
Moved all driver commands to base test class methods
hrbriggs Feb 22, 2022
9490acc
Removed try-except for open menu button
hrbriggs Feb 23, 2022
fb685d6
Catch Element not found errors and end test
hrbriggs Feb 23, 2022
10713d1
Shortened default time to wait for elements to load
hrbriggs Mar 4, 2022
1825726
Merge branch 'sony:master' into client-testing
jonathan-r-thorpe Mar 30, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Development/src/components/PaginationButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const PaginationButton = ({
};

return (
<Button onClick={() => nextPage(rel)} disabled={!enabled}>
<Button onClick={() => nextPage(rel)} disabled={!enabled} name={rel}>
{getIcon(rel)}
{label}
</Button>
Expand Down
1 change: 1 addition & 0 deletions Development/src/pages/receivers/ConnectButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const ConnectButtons = ({ senderData, receiverData }) => {
onClick={event => handleConnect('active', event)}
color="primary"
startIcon={<ActivateImmediateIcon />}
name="activate"
>
Activate
</Button>
Expand Down
1 change: 1 addition & 0 deletions Development/src/pages/receivers/ConnectionManagementTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ const ConnectionManagementTab = ({ receiverData, basePath }) => {
style={{
textDecoration: 'none',
}}
name="label"
>
<LinkChipField record={item} />
</Link>
Expand Down
2 changes: 2 additions & 0 deletions Development/src/pages/receivers/ReceiversList.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const ReceiversList = props => {
basePath="/receivers"
record={item}
label={item.label}
name="label"
/>
</TableCell>
<TableCell>
Expand All @@ -117,6 +118,7 @@ const ReceiversList = props => {
<ActiveField
record={item}
resource="receivers"
name="active"
/>
</TableCell>
)}
Expand Down
16 changes: 12 additions & 4 deletions Development/src/pages/receivers/ReceiversShow.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const ReceiversShowView = props => {
disabled={
!get(record, `$${key}`) || !useConnectionAPI
}
name={key}
/>
))}
<Tab
Expand All @@ -114,6 +115,7 @@ const ReceiversShowView = props => {
disabled={
!get(record, '$staged') || !useConnectionAPI
}
name="connect"
/>
</Tabs>
</Paper>
Expand All @@ -140,10 +142,10 @@ const ShowSummaryTab = ({ record, ...props }) => {
return (
<ShowView {...props} title={<ResourceTitle />} actions={<Fragment />}>
<SimpleShowLayout>
<TextField label="ID" source="id" />
<TextField label="ID" source="id" name="id" />
<TAIField source="version" />
<TextField source="label" />
<TextField source="description" />
<TextField source="label" name="label" />
<TextField source="description" name="description" />
<ObjectField source="tags" />
<SanitizedDivider />
<ParameterField source="transport" register={TRANSPORTS} />
Expand Down Expand Up @@ -181,7 +183,11 @@ const ShowSummaryTab = ({ record, ...props }) => {
)}
<ParameterField source="format" register={FORMATS} />
{queryVersion() >= 'v1.2' && (
<BooleanField label="Active" source="subscription.active" />
<BooleanField
label="Active"
source="subscription.active"
name="active"
/>
)}
{queryVersion() >= 'v1.2' &&
get(record, 'subscription.sender_id') && (
Expand Down Expand Up @@ -220,13 +226,15 @@ const ShowActiveTab = ({ record, ...props }) => {
source="$active.sender_id"
reference="senders"
link="show"
name="sender"
>
<LinkChipField />
</ReferenceField>
)}
<BooleanField
label="Master Enable"
source="$active.master_enable"
name="master_enable"
/>
<TextField label="Mode" source="$active.activation.mode" />
<TAIField
Expand Down
1 change: 1 addition & 0 deletions Development/src/pages/senders/SendersList.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const SendersList = props => {
basePath="/senders"
record={item}
label={item.label}
name="label"
/>
</TableCell>
<TableCell>
Expand Down
6 changes: 3 additions & 3 deletions Development/src/pages/senders/SendersShow.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ const ShowSummaryTab = ({ record, ...props }) => {
return (
<ShowView {...props} title={<ResourceTitle />} actions={<Fragment />}>
<SimpleShowLayout>
<TextField label="ID" source="id" />
<TextField label="ID" source="id" name="id" />
<TAIField source="version" />
<TextField source="label" />
<TextField source="description" />
<TextField source="label" name="label" />
<TextField source="description" name="description" />
<ObjectField source="tags" />
<SanitizedDivider />
<ParameterField source="transport" register={TRANSPORTS} />
Expand Down
1 change: 1 addition & 0 deletions Development/src/pages/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ const Settings = () => {
onFocus={selectOnFocus}
disabled={disabledSetting(QUERY_API)}
helperText="Used to show the registered Nodes and their sub-resources"
name="queryapi"
/>
</StyledListItem>
)}
Expand Down
2 changes: 2 additions & 0 deletions TestingFacade/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
max-line-length = 120
12 changes: 12 additions & 0 deletions TestingFacade/Config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Port for Testing Facade to run on
TESTING_FACADE_PORT = 5001
# URL of nmos-js instance for testing
NCUT_URL = "http://localhost:3000/#/"
# URL of NMOS Testing Tool's mock registry
MOCK_REGISTRY_URL = "http://127.0.0.1:5102/"
# Browser to use for testing. Matching webdriver must be installed
BROWSER = 'Chrome'
# Use browser in headless mode. Set to False to have each test run in a visible window
HEADLESS = True
# Time in seconds to wait for elements to load
WAIT_TIME = 5
79 changes: 79 additions & 0 deletions TestingFacade/DataStore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Copyright (C) 2021 Advanced Media Workflow Association
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

class DataStore:
"""
Store json with test question details for use with NMOS Controller
test suite and Testing Facade
"""

def __init__(self):
self.test_type = None
self.question_id = None
self.name = None
self.description = None
self.question = None
self.answers = None
self.timeout = None
self.answer_uri = None
self.answer_response = None
self.metadata = None

def clear(self):
self.test_type = None
self.question_id = None
self.name = None
self.description = None
self.question = None
self.answers = None
self.timeout = None
self.answer_uri = None
self.answer_response = None
self.metadata = None

def setJson(self, json):
self.test_type = json["test_type"]
self.question_id = json["question_id"]
self.name = json["name"]
self.description = json["description"]
self.question = json["question"]
self.answers = json["answers"]
self.timeout = json['timeout']
self.answer_uri = json["answer_uri"]
self.metadata = json["metadata"]

def getAnswerJson(self):
json_data = {
"question_id": self.question_id,
"answer_response": self.answer_response
}
return json_data

def setAnswer(self, answer):
self.answer_response = answer

def getQuestionID(self):
return self.question_id

def getAnswers(self):
return self.answers

def getUrl(self):
return self.answer_uri

def getMetadata(self):
return self.metadata


dataStore = DataStore()
128 changes: 128 additions & 0 deletions TestingFacade/GenericAutoTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
import Config as CONFIG

class GenericAutoTest:
"""
Base test class for automated version of NMOS Controller test suite without Testing Façade
"""
def __init__(self):
self.NCuT_url = CONFIG.NCUT_URL
self.mock_registry_url = CONFIG.MOCK_REGISTRY_URL
self.multipart_question_storage = {}

def set_up_test(self):
# Set up webdriver
browser = getattr(webdriver, CONFIG.BROWSER)
get_options = getattr(webdriver, CONFIG.BROWSER + 'Options', False)
if get_options:
options = get_options()
options.headless = CONFIG.HEADLESS
self.driver = browser(options=options)
else:
self.driver = browser()
self.driver.implicitly_wait(CONFIG.WAIT_TIME)
# Launch browser, navigate to nmos-js and update query api url to mock registry
self.driver.get(self.NCuT_url + "Settings")
query_api = self.driver.find_element_by_name("queryapi")
query_api.clear()
if query_api.get_attribute('value') != '':
time.sleep(1)
query_api.send_keys(Keys.CONTROL + "a")
query_api.send_keys(Keys.DELETE)
query_api.send_keys(self.mock_registry_url + "x-nmos/query/v1.3")
# Open menu to show link names if not already open
open_menu = self.driver.find_elements_by_xpath('//*[@title="Open menu"]')
if open_menu:
open_menu[0].click()

def tear_down_test(self):
self.driver.close()

def refresh_page(self):
"""
Click refresh button and sleep to allow loading time
"""
self.driver.find_element_by_css_selector("[aria-label='Refresh']").click()
time.sleep(1)

def navigate_to_page(self, page):
"""
Navigate to page by link text, refresh page and sleep to allow loading time
"""
self.driver.find_element_by_link_text(page).click()
self.refresh_page()

def find_resource_labels(self):
"""
Find all resources on a page by label
Returns list of labels
"""
resources = self.driver.find_elements_by_name("label")
return [entry.text for entry in resources]

def next_page(self):
"""
Navigate to next page via next button and sleep to allow loading time
"""
self.driver.find_element_by_name('next').click()
time.sleep(1)

def check_connectable(self):
"""
Check if connect tab is active
returns True if available, False if disabled
"""
connect_button = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.NAME,
"connect")))
disabled = connect_button.get_attribute("aria-disabled")
return True if disabled == 'false' else False

def make_connection(self, sender):
"""
Navigate to connect tab, activate connection to given sender
"""
connect = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.NAME, "connect")))
connect.click()

# Find the row containing the correct sender and activate connection
senders = self.find_resource_labels()
row = [i for i, s in enumerate(senders) if s == sender][0]
activate_button = self.driver.find_elements_by_name("activate")[row]
activate_button.click()
time.sleep(2)

def remove_connection(self, receiver):
"""
Deactivate a connection on a given receiver
"""
receivers = self.find_resource_labels()
row = [i for i, r in enumerate(receivers) if r == receiver][0]
deactivate_button = self.driver.find_elements_by_name("active")[row]
if deactivate_button.get_attribute('value') == "true":
deactivate_button.click()
time.sleep(2)

def get_active_receiver(self):
"""
Identify an active receiver
Returns string of receiver label or None
"""
active_buttons = self.driver.find_elements_by_name('active')
active_row = [i for i, b in enumerate(active_buttons) if b.get_attribute('value') == "true"]
return None if not active_row else self.driver.find_elements_by_name('label')[active_row[0]].text

def get_connected_sender(self):
"""
Identify the sender a receiver is connected to
Returns string of sender label
"""
active = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.NAME, "active")))
active.click()

return self.driver.find_element_by_name('sender').text
Loading