class TestAppianBase(unittest.TestCase): dir_path = os.path.dirname(os.path.realpath(__file__)) form_content = read_mock_file("form_content_response.json") form_content_2 = read_mock_file("sites_record_nav.json") form_content_3 = read_mock_file("sites_record_recordType_resp.json") nested_dynamic_link_json = read_mock_file( "nested_dynamic_link_response.json") def setUp(self) -> None: self.custom_locust = CustomLocust(Locust()) self.parent_task_set = TaskSet(self.custom_locust) setattr(self.parent_task_set, "host", "") setattr(self.parent_task_set, "credentials", [["", ""]]) setattr(self.parent_task_set, "auth", ["a", "b"]) self.task_set = AppianTaskSet(self.parent_task_set) self.task_set.host = "" self.task_set.on_start() def tearDown(self) -> None: if '__appianMultipartCsrfToken' in self.task_set.appian.client.cookies: self.task_set.on_stop() def test_determine_auth_with_only_auth_key(self) -> None: # Given for bad_creds in [[], "", None, 30]: setattr(self.parent_task_set, "credentials", bad_creds) # When auth = self.task_set.determine_auth() # Then self.assertEqual(["a", "b"], auth) def test_determine_auth_with_only_credentials_key(self) -> None: # Given setattr(self.parent_task_set, "auth", "") setattr(self.parent_task_set, "credentials", [["aa", "bb"], ["c", "d"]]) # When the first hit is done auth1 = self.task_set.determine_auth() auth2 = self.task_set.determine_auth() auth3 = self.task_set.determine_auth() auth4 = self.task_set.determine_auth() # Then self.assertEqual(["aa", "bb"], auth1) # Pop is FIFO self.assertEqual(["c", "d"], auth2) self.assertEqual(["c", "d"], auth3) self.assertEqual(["c", "d"], auth4) def test_determine_auth_with_credentials_and_auth_keys(self) -> None: # Given setattr(self.parent_task_set, "credentials", [["aa", "bb"], ["c", "d"]]) # When the first hit is done auth1 = self.task_set.determine_auth() auth2 = self.task_set.determine_auth() auth3 = self.task_set.determine_auth() auth4 = self.task_set.determine_auth() # Then self.assertEqual(["aa", "bb"], auth1) # Pop is FIFO self.assertEqual(["c", "d"], auth2) self.assertEqual(["a", "b"], auth3) self.assertEqual(["a", "b"], auth4) def test_login_good_auth(self) -> None: # Given init_cookies = {'JSESSIONID': 'abc', '__appianCsrfToken': '123'} cookies = { 'JSESSIONID': 'abc123', '__appianCsrfToken': 'different cookie', '__appianMultipartCsrfToken': 'these cookies' } self.custom_locust.set_response("/suite/", 200, '<html>A huge html blob</html>', cookies=init_cookies) self.custom_locust.set_response("/suite/auth?appian_environment=tempo", 200, '<html>A huge html blob</html>', cookies=cookies) self.task_set.appian.login(["", ""]) self.assertEqual(cookies, self.task_set.appian.client.cookies) def test_login_bad_auth(self) -> None: # Given self.custom_locust.set_response( "/suite/auth?appian_environment=tempo", 401, 'The username/password entered is invalid') with self.assertRaisesRegex(BadCredentialsException, "Could not log in"): self.task_set.appian.login(["", ""]) def test_login_bad_auth_bad_cookies(self) -> None: # Given cookies = {'JSESSIONID': 'abc', '__appianCsrfToken': '123'} self.custom_locust.set_response("/suite/", 200, '<html>A huge html blob</html>', cookies=cookies) self.custom_locust.set_response("/suite/auth?appian_environment=tempo", 200, '<html>A huge html blob</html>', cookies=cookies) with self.assertRaisesRegex( MissingCsrfTokenException, "Login unsuccessful, no multipart cookie found"): self.task_set.on_start() def test_instantiating_task_set(self) -> None: # Given ts = SampleAppianTaskSequence(self.custom_locust) # When task_name_1 = ts.get_next_task().__name__ # Then self.assertEqual("first_task", task_name_1) # When called again task_name_2 = ts.get_next_task().__name__ # Then the next task self.assertEqual("second_task", task_name_2) # When called the last time task_name_3 = ts.get_next_task().__name__ # Then it wraps around self.assertEqual("first_task", task_name_3) def test_appian_client_on_its_own(self) -> None: # Given inner_client = MockClient() dir_path = os.path.dirname(os.path.realpath(__file__)) actions = read_mock_file("actions_response.json") host = "https://my-fake-host.com" inner_client.set_response( f"{host}/suite/api/tempo/open-a-case/available-actions?ids=%5B%5D", 200, actions) appian_client = AppianClient(inner_client, "https://my-fake-host.com") # When client, resp = appian_client.login(["a", "1"]) appian_client.actions.get_all() # Then self.assertEqual(200, resp.status_code) def test_procedurally_generate_credentials(self) -> None: # Given CONFIG = { "procedural_credentials_prefix": "employee", "procedural_credentials_count": 3, "procedural_credentials_password": "******" } expected_credentials = [['employee1', 'pass'], ['employee2', 'pass'], ['employee3', 'pass']] # When procedurally_generate_credentials(CONFIG) # Then self.assertEqual(expected_credentials, CONFIG["credentials"]) def test_procedurally_generate_credentials_keys_missing(self) -> None: # Given CONFIG = { "procedural_credentials_prefix": "employee", "procedural_credentials_password": "******" } # When with self.assertRaisesRegex(MissingConfigurationException, '["procedural_credentials_count"]'): procedurally_generate_credentials(CONFIG) def test_procedurally_generate_credentials_multiple_keys_missing( self) -> None: # Given CONFIG2 = {'unrelated': 'config'} # When with self.assertRaisesRegex( MissingConfigurationException, '["procedural_credentials_prefix", "procedural_credentials_count", "procedural_credentials_password"]' ): procedurally_generate_credentials(CONFIG2) def test_setup_distributed_creds(self) -> None: # Given CONFIG = { "credentials": [['employee1', 'pass'], ['employee2', 'pass'], ['employee3', 'pass']] } os.environ["STY"] = "64331.locustdriver-2-0" expected_config = [['employee1', 'pass'], ['employee3', 'pass']] # When setup_distributed_creds(CONFIG) self.assertEqual(CONFIG["credentials"], expected_config) def test_setup_distributed_creds_2(self) -> None: # Given CONFIG = { "credentials": [['employee1', 'pass'], ['employee2', 'pass'], ['employee3', 'pass']] } os.environ["STY"] = "64331.locustdriver-2-1" expected_config = [['employee2', 'pass']] # When setup_distributed_creds(CONFIG) self.assertEqual(CONFIG["credentials"], expected_config) def test_setup_distributed_creds_fewer_credentials_than_workers( self) -> None: # Given CONFIG = {"credentials": [['employee1', 'pass']]} os.environ["STY"] = "64331.locustdriver-2-1" expected_config = [['employee1', 'pass']] # When setup_distributed_creds(CONFIG) self.assertEqual(CONFIG["credentials"], expected_config) def test_setup_distributed_creds_fails_missing_key(self) -> None: # Given CONFIG = {'unrelated': 'config'} # When with self.assertRaisesRegex(MissingConfigurationException, '["credentials"]'): setup_distributed_creds(CONFIG)
class TestTask(unittest.TestCase): task_feed_resp = read_mock_file("tasks_response.json") task_feed_with_next = read_mock_file("tasks_response_with_next.json") def get_task_attributes(self, is_auto_acceptable: bool) -> str: return f""" {{ "isOfflineTask": false, "isSailTask": true, "isQuickTask": false, "taskId": "1", "isAutoAcceptable": {'true' if is_auto_acceptable else 'false'} }}""" def setUp(self) -> None: self.custom_locust = CustomLocust(Locust()) parent_task_set = TaskSet(self.custom_locust) setattr(parent_task_set, "host", "") setattr(parent_task_set, "auth", ["", ""]) self.task_set = AppianTaskSet(parent_task_set) self.task_set.host = "" # test_on_start_auth_success is covered here. self.custom_locust.set_response("auth?appian_environment=tempo", 200, '{}') self.custom_locust.set_default_response(200, "{}") self.task_set.on_start() self.custom_locust.set_response( "/suite/rest/a/task/latest/1/attributes", 200, self.get_task_attributes(is_auto_acceptable=True)) self.custom_locust.set_response(_Tasks.INITIAL_FEED_URI, 200, self.task_feed_resp) def tearDown(self) -> None: self.task_set.on_stop() def test_task_get_all(self) -> None: self.custom_locust.enqueue_response(200, self.task_feed_with_next) self.custom_locust.enqueue_response(200, self.task_feed_resp) all_task = self.task_set.appian.tasks.get_all() self.assertEqual(len(all_task.keys()), 21) # TODO: These keys are bad, as are the record/ report keys, consider just using the names/ IDs self.assertEqual( all_task['t-2109072_34 Task_Assign SR#0G56-W7N3 to Case Worker'] ['id'], 't-2109072') def test_task_get(self) -> None: task = self.task_set.appian.tasks.get_task("t-1", False) self.assertIsInstance(task, dict) def test_task_get_missing_task(self) -> None: with self.assertRaisesRegex( Exception, "There is no task with name .* in the system under test.*"): self.task_set.appian.tasks.get_task("some random word") def test_task_visit(self) -> None: output = self.task_set.appian.tasks.visit("t-1", False) self.assertEqual(output, {}) def test_task_visit_and_get_form(self) -> None: task_to_accept = read_mock_file('task_accept_resp.json') self.custom_locust.set_response("/suite/rest/a/task/latest/1/status", 200, task_to_accept) self.custom_locust.set_response( "/suite/rest/a/task/latest/1/attributes", 200, self.get_task_attributes(is_auto_acceptable=False)) output = self.task_set.appian.tasks.visit_and_get_form("t-1", False) self.assertEqual(output.form_url, "/suite/rest/a/task/latest/1/form")
class TestSites(unittest.TestCase): def setUp(self) -> None: self.custom_locust = CustomLocust(Locust()) parent_task_set = TaskSet(self.custom_locust) setattr(parent_task_set, "host", "") setattr(parent_task_set, "auth", ["", ""]) self.task_set = AppianTaskSet(parent_task_set) self.task_set.host = "" # test_on_start_auth_success is covered here. self.custom_locust.set_response("auth?appian_environment=tempo", 200, '{}') self.task_set.on_start() all_sites_str = read_mock_file("all_sites.json") self.custom_locust.set_response(_Sites.TEMPO_SITE_PAGE_NAV, 200, all_sites_str) # Default responses are page responses page_resp_json = read_mock_file("page_resp.json") self.custom_locust.client.set_default_response(200, page_resp_json) def tearDown(self) -> None: self.task_set.on_stop() def test_sites_get_all(self) -> None: page_resp_json = read_mock_file("page_resp.json") site_nav_resp = read_mock_file("sites_nav_resp.json") all_sites_str = read_mock_file("all_sites.json") self.custom_locust.client.enqueue_response(200, all_sites_str) for i in range(136): self.custom_locust.client.enqueue_response(200, site_nav_resp) for i in range(5): self.custom_locust.client.enqueue_response(200, page_resp_json) self.task_set.appian.sites.get_all() all_sites = self.task_set.appian.sites._sites self.assertEqual(len(all_sites.keys()), 136) self.assertTrue("rla" in all_sites, "rla not found in list of sites") # Spot check rla_site = all_sites["rla"] self.assertEqual(len(rla_site.pages.keys()), 5) self.assertEqual(rla_site.pages['create-mrn'].page_type, PageType.REPORT) def set_sites_json(self, site_name: str) -> None: sites_nav_resp = read_mock_file("sites_nav_resp.json") self.custom_locust.set_response( f"/suite/rest/a/sites/latest/{site_name}/nav", 200, sites_nav_resp) def test_sites_get(self) -> None: site_name = 'mrn' self.set_sites_json(site_name) site = self.task_set.appian.sites.get_site_data_by_site_name('mrn') def test_sites_link_type(self) -> None: for type_pair in [('InternalActionLink', 'action'), ('InternalReportLink', 'report'), ('SiteRecordTypeLink', 'recordType')]: original_link_type = type_pair[0] expected_link_type = type_pair[1] link_full = f"{{http://www.host.net/ae/types/2009}}{original_link_type}" link_type = self.task_set.appian.sites._get_type_from_link_type( link_full) self.assertEqual(link_type.value, expected_link_type) def test_sites_bad_link_type(self) -> None: with self.assertRaises(Exception) as e: bad_link_type = "this is garbage" self.task_set.appian.sites._get_type_from_link_type(bad_link_type) self.assertEqual(e.exception.args[0], f"Invalid Link Type: {bad_link_type}") def test_navigate_to_tab_error_cases(self) -> None: site_name = "abc" self.set_sites_json(site_name) with self.assertRaises(SiteNotFoundException): self.task_set.appian.sites.navigate_to_tab("other_site", "123") with self.assertRaises(PageNotFoundException): self.task_set.appian.sites.navigate_to_tab(site_name, "123") def test_navigate_to_tab_success(self) -> None: site_name = "abc" page_name = "create-mrn" link_type = "report" # Default Page Info self.set_sites_json(site_name) self.custom_locust.set_response( f"/suite/rest/a/sites/latest/{site_name}/pages/{page_name}/{link_type}", 200, '{"test":"abc"}') tab_resp = self.task_set.appian.sites.navigate_to_tab( site_name, page_name) self.assertEqual({'test': 'abc'}, tab_resp.json()) def test_visit_and_get_form_success(self) -> None: site_name = "abc" page_name = "create-mrn" link_type = "report" # Default Page Info self.set_sites_json(site_name) expected_uuid = 'abc123' expected_context = '{"abc":"123"}' form_content = f'{{"context":{expected_context}, "uuid":"{expected_uuid}"}}' expected_url = f"/suite/rest/a/sites/latest/{site_name}/pages/{page_name}/{link_type}" self.custom_locust.set_response(expected_url, 200, form_content) ui_form: SailUiForm = self.task_set.appian.sites.visit_and_get_form( site_name, page_name) self.assertEqual(expected_uuid, ui_form.uuid) self.assertEqual(json.loads(expected_context), ui_form.context) self.assertEqual(expected_url, ui_form.form_url) def test_navigate_to_tab_and_record_report_success(self) -> None: site_name = "abc" page_name = "create-mrn" link_type = "report" # Default Page Info self.set_sites_json(site_name) self.custom_locust.set_response( f"/suite/rest/a/sites/latest/{site_name}/pages/{page_name}/{link_type}", 200, '{"test":"abc"}') tab_resp = self.task_set.appian.sites.navigate_to_tab_and_record_if_applicable( site_name, page_name) self.assertEqual({'test': 'abc'}, tab_resp.json()) def test_get_sites_records(self) -> None: site_name = "orders" page_name = "orders" link_type = "recordType" nav_resp = read_mock_file("sites_record_nav.json") page_resp = read_mock_file("sites_record_page_resp.json") record_resp = read_mock_file("sites_record_recordType_resp.json") for endpoint in [ f"/suite/rest/a/sites/latest/{site_name}/nav", f"/suite/rest/a/sites/latest/{site_name}/pages/{page_name}/nav" ]: self.custom_locust.set_response(endpoint, 200, nav_resp) nav_ui = json.loads(nav_resp) for mocked_page_name in self.task_set.appian.sites.get_page_names_from_ui( nav_ui): self.custom_locust.set_response( f"/suite/rest/a/applications/latest/legacy/sites/{site_name}/page/{mocked_page_name}", 200, page_resp) self.custom_locust.set_response( f"/suite/rest/a/sites/latest/{site_name}/pages/{page_name}/{link_type}", 200, record_resp) self.custom_locust.set_default_response(200, record_resp) resp = self.task_set.appian.sites.navigate_to_tab_and_record_if_applicable( site_name, page_name) self.assertEqual(resp.text, record_resp)
class TestActions(unittest.TestCase): actions = read_mock_file("actions_response.json") action_under_test = "Create a Case::koBOPgHGLIgHRQzrdseY6-wW_trk0FY-87TIog3LDZ9dbSn9dYtlSaOQlWaz7PcZgV5FWdIgYk8QRlv1ARbE4czZL_8fj4ckCLzqA" def setUp(self) -> None: self.custom_locust = CustomLocust(Locust()) parent_task_set = TaskSet(self.custom_locust) setattr(parent_task_set, "host", "") setattr(parent_task_set, "auth", ["", ""]) self.task_set = AppianTaskSet(parent_task_set) self.task_set.host = "" # test_on_start_auth_success is covered here. self.custom_locust.set_response("auth?appian_environment=tempo", 200, '{}') self.task_set.on_start() self.custom_locust.set_response( "/suite/api/tempo/open-a-case/available-actions?ids=%5B%5D", 200, self.actions) def tearDown(self) -> None: self.task_set.on_stop() def test_actions_get_all(self) -> None: all_actions = self.task_set.appian.actions.get_all() self.assertTrue(len(list(all_actions.keys())) > 0) def test_actions_get(self) -> None: action = self.task_set.appian.actions.get_action( self.action_under_test) self.assertEqual(action['displayLabel'], 'Create a Case') def test_actions_get_corrupt_action(self) -> None: corrupt_actions = self.actions.replace( '"opaqueId": "koBOPgHGLIgHRQzrdseZ66wChtz5aQqM_RBTDeSBi9lWr4b18XPJqrikBSQYzzp8_e2Wgw0ku-apJjK94StAV1R3DU5zipwSXfCTA"', '"corrupt_opaqueId": "koBOPgHGLIgHRQzrdseZ66wChtz5aQqM_RBTDeSBi9lWr4b18XPJqrikBSQYzzp8_e2Wgw0ku-apJjK94StAV1R3DU5zipwSXfCTA"' ) self.custom_locust.set_response( "/suite/api/tempo/open-a-case/available-actions?ids=%5B%5D", 200, corrupt_actions) all_actions = self.task_set.appian.actions.get_all() self.assertTrue("ERROR::1" in str(all_actions)) self.assertTrue(self.task_set.appian.actions._errors == 1) def test_actions_zero_actions(self) -> None: corrupt_actions = self.actions.replace('"actions"', '"nonexistent_actions"') self.custom_locust.set_response( "/suite/api/tempo/open-a-case/available-actions?ids=%5B%5D", 200, corrupt_actions) all_actions = self.task_set.appian.actions.get_all() self.assertTrue(all_actions == {}) def test_actions_get_partial_match(self) -> None: action = self.task_set.appian.actions.get_action( "Create a Case", False) self.assertEqual(action['displayLabel'], 'Create a Case') def test_actions_get_multiple_matches(self) -> None: self.task_set.appian.actions._actions = dict() # Resetting the cache. action = self.task_set.appian.actions.get_action("Create a C", False) self.assertTrue("Create a C" in action['displayLabel']) def test_actions_get_missing_action(self) -> None: with self.assertRaisesRegex( Exception, "There is no action with name .* in the system under test.*"): self.task_set.appian.actions.get_action("Create a Case", exact_match=True) def setup_action_response_no_ui(self) -> None: action = self.task_set.appian.actions.get_action( "Create a Case", False) self.custom_locust.set_response(action['formHref'], 200, "{}") def setup_action_response_with_ui( self, file_name: str = "form_content_response.json") -> None: action = self.task_set.appian.actions.get_action( "Create a Case", False) resp_json = read_mock_file(file_name) self.custom_locust.set_response(action['formHref'], 200, resp_json) def test_actions_visit(self) -> None: self.setup_action_response_no_ui() action = self.task_set.appian.actions.visit("Create a Case", False) self.assertIsInstance(action, dict) def test_actions_start(self) -> None: self.setup_action_response_no_ui() self.task_set.appian.actions.start_action(self.action_under_test) def test_actions_start_skip_design_call(self) -> None: self.task_set.appian.actions.start_action(self.action_under_test, True) def test_actions_form_example_success(self) -> None: # output of get_page of a form (SAIL) self.setup_action_response_with_ui() self.custom_locust.set_response( '/suite/rest/a/model/latest/228/form', 200, '{"context":"12345","ui": {"#t": "UiComponentsDelta", "modifiedComponents":[]}}' ) sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) label = 'Title' value = "Look at me, I am filling out a form" button_label = 'Submit' latest_form = sail_form.fill_text_field(label, value).click(button_label) resp = latest_form.get_response() self.assertEqual("12345", resp['context']) def test_actions_form_example_activity_chained(self) -> None: action = self.task_set.appian.actions.get_action( "Create a Case", False) resp_json = read_mock_file("form_content_response.json") self.custom_locust.set_response( action['formHref'], 200, '{"mobileEnabled": "false", "empty": "true", "formType": "START_FORM"}' ) self.custom_locust.set_response(action['initiateActionHref'], 200, resp_json) self.custom_locust.set_response( '/suite/rest/a/model/latest/228/form', 200, '{"context":"12345","ui": {"#t": "UiComponentsDelta", "modifiedComponents":[]}}' ) sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case") label = 'Title' value = "Look at me, I am filling out a form" button_label = 'Submit' latest_form = sail_form.fill_text_field(label, value).click(button_label) resp = latest_form.get_response() self.assertEqual("12345", resp['context']) def test_actions_form_example_missing_field(self) -> None: self.setup_action_response_with_ui() sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) value = "Look at me, I am filling out a form" label = "missingText" with self.assertRaises(ComponentNotFoundException) as context: sail_form.fill_text_field(label, value) self.assertEqual( context.exception.args[0], f"Could not find the component with label '{label}' in the provided form" ) button_label = 'press me' with self.assertRaises(ComponentNotFoundException) as context: sail_form.click(button_label) self.assertEqual( context.exception.args[0], f"Could not find the component with label '{button_label}' in the provided form" ) def test_actions_form_example_bad_response(self) -> None: self.setup_action_response_with_ui() sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) self.custom_locust.set_response('/suite/rest/a/model/latest/228/form', 200, 'null') value = "Look at me, I am filling out a form" label = "Title" with self.assertRaises(Exception) as context: sail_form.fill_text_field(label, value) self.assertEqual( context.exception.args[0], f"No response returned when trying to update field with label '{label}'" ) button_label = 'Submit' with self.assertRaises(Exception) as context: sail_form.click(button_label) self.assertEqual( context.exception.args[0], f"No response returned when trying to click button with label '{button_label}'" ) def test_actions_form_dropdown_errors(self) -> None: self.setup_action_response_with_ui('dropdown_test_ui.json') sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) dropdown_label = "missing dropdown" with self.assertRaises(ComponentNotFoundException) as context: sail_form.select_dropdown_item(dropdown_label, 'some choice') self.assertEqual( context.exception.args[0], f"Could not find the component with label '{dropdown_label}' in the provided form" ) dropdown_label = "Name" with self.assertRaises(InvalidComponentException): sail_form.select_dropdown_item(dropdown_label, 'some choice') dropdown_label = "Customer Type" with self.assertRaises(ChoiceNotFoundException): sail_form.select_dropdown_item(dropdown_label, 'some missing choice') @patch('appian_locust.SailUiForm._get_update_url_for_reeval', return_value="/mocked/re-eval/url") @patch('appian_locust.uiform._Interactor.send_dropdown_update') def test_actions_form_dropdown_success( self, mock_send_dropdown_update: MagicMock, mock_get_update_url_for_reeval: MagicMock) -> None: self.setup_action_response_with_ui('dropdown_test_ui.json') sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) initial_state = sail_form.state dropdown_label = "Customer Type" sail_form.select_dropdown_item(dropdown_label, 'Buy Side Asset Manager') mock_get_update_url_for_reeval.assert_called_once_with(initial_state) mock_send_dropdown_update.assert_called_once() args, kwargs = mock_send_dropdown_update.call_args self.assertEqual(args[0], "/mocked/re-eval/url") self.assertIsNone(kwargs["url_stub"]) self.assertNotEqual(sail_form.state, initial_state) @patch('appian_locust.SailUiForm._get_update_url_for_reeval', return_value="/mocked/re-eval/url") @patch('appian_locust.uiform._Interactor.send_dropdown_update') def test_actions_form_record_list_dropdown_success( self, mock_send_dropdown_update: MagicMock, mock_get_update_url_for_reeval: MagicMock) -> None: # 'dropdown_test_record_list_ui.json' contains a 'sail-application-url' field self.setup_action_response_with_ui('dropdown_test_record_list_ui.json') sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) initial_state = sail_form.state dropdown_label = "Customer Type" sail_form.select_dropdown_item(dropdown_label, 'Buy Side Asset Manager') mock_get_update_url_for_reeval.assert_called_once_with(initial_state) mock_send_dropdown_update.assert_called_once() args, kwargs = mock_send_dropdown_update.call_args self.assertEqual(args[0], "/mocked/re-eval/url") self.assertEqual(kwargs["url_stub"], "url_stub123") self.assertNotEqual(sail_form.state, initial_state) @patch('appian_locust.uiform._Interactor.send_multiple_dropdown_update') def test_multiple_dropdown_not_found( self, mock_send_multiple_dropdown_update: MagicMock) -> None: self.setup_action_response_with_ui('dropdown_test_ui.json') sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) dropdown_label = "Regions wrong label" with self.assertRaises(ComponentNotFoundException) as context: sail_form.select_multi_dropdown_item(dropdown_label, ["Asia"]) self.assertEqual( context.exception.args[0], f"Could not find the component with label '{dropdown_label}' in the provided form" ) dropdown_label = "Regions" sail_form.select_multi_dropdown_item(dropdown_label, ["Asia"]) mock_send_multiple_dropdown_update.assert_called_once() args, kwargs = mock_send_multiple_dropdown_update.call_args @patch('appian_locust.uiform.find_component_by_attribute_in_dict') @patch('appian_locust.uiform._Interactor.select_radio_button') def test_actions_form_radio_button_by_label_success( self, mock_select_radio_button: MagicMock, mock_find_component_by_label: MagicMock) -> None: self.setup_action_response_with_ui('dropdown_test_ui.json') sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) state1 = sail_form.state button_label = "test-radio-button" sail_form.select_radio_button_by_test_label(button_label, 1) button_label = "Qualified Institutional Buyer" sail_form.select_radio_button_by_label(button_label, 1) args, kwargs = mock_find_component_by_label.call_args_list[0] self.assertEqual('testLabel', args[0]) args_next_call, kwargs_next_call = mock_find_component_by_label.call_args_list[ 1] self.assertEqual('label', args_next_call[0]) self.assertEqual(2, len(mock_select_radio_button.call_args_list)) def test_actions_form_radio_button_by_label_error(self) -> None: self.setup_action_response_with_ui('dropdown_test_ui.json') sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) button_label = "missing button" with self.assertRaises(ComponentNotFoundException) as context: sail_form.select_radio_button_by_test_label(button_label, 1) self.assertEqual( context.exception.args[0], f"Could not find the component with label '{button_label}' in the provided form" ) with self.assertRaises(ComponentNotFoundException) as context: sail_form.select_radio_button_by_label(button_label, 1) self.assertEqual( context.exception.args[0], f"Could not find the component with label '{button_label}' in the provided form" ) @patch('appian_locust.uiform.find_component_by_index_in_dict') @patch('appian_locust.uiform._Interactor.select_radio_button') def test_actions_form_radio_button_by_index_success( self, mock_select_radio_button: MagicMock, mock_find_component_by_index: MagicMock) -> None: self.setup_action_response_with_ui('dropdown_test_ui.json') sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) initial_state = sail_form.state button_index = 1 sail_form.select_radio_button_by_index(button_index, 1) mock_select_radio_button.assert_called_once() mock_find_component_by_index.assert_called_with( 'RadioButtonField', button_index, initial_state) self.assertNotEqual(sail_form.state, initial_state) def test_actions_form_radio_button_by_index_error(self) -> None: self.setup_action_response_with_ui('dropdown_test_ui.json') sail_form: SailUiForm = self.task_set.appian.actions.visit_and_get_form( "Create a Case", False) index_too_low = -1 with self.assertRaises(Exception) as context: sail_form.select_radio_button_by_index(index_too_low, 1) self.assertEqual( context.exception.args[0], f"Invalid index: '{index_too_low}'. Please enter a positive number" ) index_too_high = 4 with self.assertRaises(Exception) as context: sail_form.select_radio_button_by_index(index_too_high, 1) self.assertEqual( context.exception.args[0], f"Bad index: only '3' components of type 'RadioButtonField' found on page, requested '{index_too_high}'" ) index_invalid = "bad index" with self.assertRaises(Exception) as context: sail_form.select_radio_button_by_index(index_invalid, 1) self.assertEqual( context.exception.args[0], f"Invalid index: '{index_invalid}'. Please enter a positive number" )
class TestNews(unittest.TestCase): news = read_mock_file("news_response.json") def setUp(self) -> None: self.custom_locust = CustomLocust(Locust()) parent_task_set = TaskSet(self.custom_locust) setattr(parent_task_set, "host", "") setattr(parent_task_set, "auth", ["", ""]) self.task_set = AppianTaskSet(parent_task_set) self.task_set.host = "" # test_on_start_auth_success is covered here. self.custom_locust.set_response("auth?appian_environment=tempo", 200, '{}') self.task_set.on_start() self.custom_locust.set_response(NEWS_URI, 200, self.news) def tearDown(self) -> None: self.task_set.on_stop() def test_news_get_all(self) -> None: all_news = self.task_set.appian.news.get_all() self.assertIsInstance(all_news, dict) def test_news_search(self) -> None: self.custom_locust.set_response("/suite/api/feed/tempo?q=appian", 200, self.news) all_news = self.task_set.appian.news.search("appian") self.assertIsInstance(all_news, dict) def test_news_get(self) -> None: news = self.task_set.appian.news.get_news( "x-1::Administrator Custom") self.assertIsInstance(news, dict) def test_news_get_corrupt_news_post(self) -> None: corrupt_news = self.news.replace('"id": "x-3"', '"corrupt_id": "x-3"') self.custom_locust.set_response(NEWS_URI, 200, corrupt_news) all_news = self.task_set.appian.news.get_all() self.assertTrue("ERROR::1" in str(all_news)) self.assertTrue(self.task_set.appian.news._errors == 1) def test_actions_get_retry(self) -> None: self.task_set.appian.news._news = dict() # Resetting the cache. self.custom_locust.set_response("/suite/api/feed/tempo?q=Admin", 200, self.news) action = self.task_set.appian.news.get_news( "x-1::Administrator Custom", True, "Admin") self.assertIsInstance(action, dict) def test_news_get_missing_news(self) -> None: with self.assertRaisesRegex(Exception, "There is no news with name .* in the system under test.*"): self.task_set.appian.news.get_news("some random word") def test_news_visit(self) -> None: self.task_set.appian.news.visit("x-1", False) def test_news_visit_entry(self) -> None: self.custom_locust.set_response("/suite/api/feed/tempo?q=appian", 200, self.news) self.assertEqual((200, 200), self.task_set.appian.news.visit_news_entry("x-1", False)) def test_news_visit_entry_no_share_link(self) -> None: self.custom_locust.set_response("/suite/api/feed/tempo?q=appian", 200, self.news) self.assertEqual(self.task_set.appian.news.visit_news_entry("x-2", False), (None, 200)) def test_news_visit_entry_no_edit_link(self) -> None: self.custom_locust.set_response("/suite/api/feed/tempo?q=appian", 200, self.news) self.assertEqual(self.task_set.appian.news.visit_news_entry("x-3", False), (200, None)) def test_news_get_all_logic(self) -> None: test_cases = [ { "input": '{"feed":{"entries":[{"title": "valid_test", "id": "123"}]}}', "expected": {'123::valid_test': {'title': 'valid_test', 'id': '123'}} }, { "input": '{"feed":{"not_entries":[{"title": "valid_test", "id": "123"}]}}', "expected": {} }, { "input": '{"not_feed":{"entries":[{"title": "valid_test", "id": "123"}]}}', "expected": {} }, { "input": '{"feed":{"entries":[{"not_title": "valid_test", "id": "123"}]}}', "expected": {} } ] for test_case in test_cases: # Given self.custom_locust.set_response(NEWS_URI, 200, str(test_case['input'])) # When response = self.task_set.appian.news.get_all() # Then self.assertEqual(response, test_case['expected'])
class TestReports(unittest.TestCase): reports = read_mock_file("reports_response.json") def setUp(self) -> None: self.custom_locust = CustomLocust(Locust()) parent_task_set = TaskSet(self.custom_locust) setattr(parent_task_set, "host", "") setattr(parent_task_set, "auth", ["", ""]) self.task_set = AppianTaskSet(parent_task_set) self.task_set.host = "" # test_on_start_auth_success is covered here. self.custom_locust.set_response("auth?appian_environment=tempo", 200, '{}') self.task_set.on_start() self.custom_locust.set_response( "/suite/rest/a/uicontainer/latest/reports", 200, self.reports) def tearDown(self) -> None: self.task_set.on_stop() def test_reports_get_all(self) -> None: all_reports = self.task_set.appian.reports.get_all() self.assertIsInstance(all_reports, dict) def test_reports_get(self) -> None: reports = self.task_set.appian.reports.get_report( "RTE Basic Test Report::qdjDPA") self.assertIsInstance(reports, dict) def test_reports_get_corrupt_report(self) -> None: corrupt_reports = self.reports.replace( '"title": "!!SAIL test charts"', '"corrupt_title": "!!SAIL test charts"') self.custom_locust.set_response( "/suite/rest/a/uicontainer/latest/reports", 200, corrupt_reports) all_reports = self.task_set.appian.reports.get_all() self.assertTrue("ERROR::1" in str(all_reports)) self.assertEqual(1, self.task_set.appian.reports._errors) def test_reports_zero_reports(self) -> None: corrupt_reports = self.reports.replace('"entries"', '"nonexistent_entries"') self.custom_locust.set_response( "/suite/rest/a/uicontainer/latest/reports", 200, corrupt_reports) all_reports = self.task_set.appian.reports.get_all() self.assertTrue(all_reports == {}) def test_reports_get_missing_report(self) -> None: with self.assertRaisesRegex( Exception, "There is no report with name .* in the system under test.*"): self.task_set.appian.reports.get_report("some random word") def test_reports_visit(self) -> None: self.custom_locust.set_response( "/suite/rest/a/sites/latest/D6JMim/pages/reports/report/qdjDPA/reportlink", 200, "{}") output = self.task_set.appian.reports.visit( "RTE Basic Test Report::qdjDPA") self.assertEqual(output, dict()) def test_reports_form_example_success(self) -> None: self.custom_locust.set_response( "/suite/rest/a/sites/latest/D6JMim/pages/reports/report/qdjDPA/reportlink", 200, '{"context":123, "uuid":123}') sail_form = self.task_set.appian.reports.visit_and_get_form( "RTE Basic Test Report", False) self.assertTrue(isinstance(sail_form, SailUiForm)) def test_reports_form_example_failure(self) -> None: self.custom_locust.set_response( "/suite/rest/a/sites/latest/D6JMim/pages/reports/report/qdjDPA/reportlink", 200, '{"context":123, "uuid":123}') report_name = "Fake Report" exact_match = False with self.assertRaises(Exception) as context: self.task_set.appian.reports.visit_and_get_form( report_name, exact_match) self.assertEqual( context.exception.args[0], f"There is no report with name {report_name} in the system under test (Exact match = {exact_match})" )
class TestRecords(unittest.TestCase): record_types = read_mock_file("record_types_response.json") # Record Insance List for a specific RecordType records = read_mock_file("records_response.json") # Record Summary dashboard response for a specific Record Instance record_summary_view = read_mock_file("record_summary_view_response.json") record_instance_name = "Actions Page" def setUp(self) -> None: self.custom_locust = CustomLocust(Locust()) parent_task_set = TaskSet(self.custom_locust) setattr(parent_task_set, "host", "") setattr(parent_task_set, "auth", ["", ""]) self.task_set = AppianTaskSet(parent_task_set) self.task_set.host = "" # test_on_start_auth_success is covered here. self.custom_locust.set_response("auth?appian_environment=tempo", 200, '{}') if not hasattr(self.task_set, 'appian'): self.task_set.on_start() self.custom_locust.set_response( "/suite/rest/a/applications/latest/app/records/view/all", 200, self.record_types) self.custom_locust.set_response( "/suite/rest/a/sites/latest/D6JMim/pages/records/recordType/commit", 200, self.records) self.custom_locust.set_response( "/suite/rest/a/applications/latest/legacy/tempo/records/type/commit/view/all", 200, self.records) self.custom_locust.set_response( "/suite/rest/a/sites/latest/D6JMim/page/records/record/lQB0K7YxC0UQ2Fhx4pmY1F49C_MjItD4hbtRdKDmOo6V3MOBxI47ipGa_bJKZf86CLtvOCp1cfX-sa2O9hp6WTKZpbGo5MxRaaTwMkcYMeDl8kN8Hg/view/summary", 200, self.record_summary_view) def tearDown(self) -> None: self.task_set.on_stop() def test_records_get_all(self) -> None: all_records = self.task_set.appian.records.get_all() self.assertIsInstance(all_records, dict) def test_records_get_all_http_error(self) -> None: self.custom_locust.set_response( "/suite/rest/a/sites/latest/D6JMim/pages/records/recordType/commit", 500, self.records) with self.assertLogs(level="WARN") as msg: self.task_set.appian.records.get_all() self.assertIn("500 Server Error", msg.output[0]) def test_records_get_all_bad_response(self) -> None: # setting up bad response temp_record_types = self.record_types.replace( '"rel":"x-web-bookmark"', '"rel":"bad_x-web-bookmark_bad"').replace( '"#t":"CardLayout"', '"#t":"bad_CardLayout_bad"') self.custom_locust.set_response( "/suite/rest/a/applications/latest/app/records/view/all", 200, temp_record_types) with (self.assertRaisesRegex( Exception, "Unexpected response on Get call of All Records")): self.task_set.appian.records.get_all() def test_records_get_corrupt_records(self) -> None: # setting up bad response corrupt_records = self.records.replace( '"_recordRef": "lQB0K7YxC0UQ2Fhx4pmY1F49C_MjItD4hbtRdKDmOo6V3MOBxI47ipGa_bJKZf86CLtvOCp1cfX-sa2O9hu7mTKKv82PEWF9q1lBcroZD_VQpwcQDs"', '"corrupt_recordRef": "lQB0K7YxC0UQ2Fhx4pmY1F49C_MjItD4hbtRdKDmOo6V3MOBxI47ipGa_bJKZf86CLtvOCp1cfX-sa2O9hu7mTKKv82PEWF9q1lBcroZD_VQpwcQDs"' ) self.custom_locust.set_response( "/suite/rest/a/sites/latest/D6JMim/pages/records/recordType/commit", 200, corrupt_records) all_records = self.task_set.appian.records.get_all() self.assertTrue("ERROR::1" in str(all_records)) self.assertTrue(self.task_set.appian.records._errors == 1) def test_records_get_all_mobile(self) -> None: all_records = self.task_set.appian.records.get_all_mobile() self.assertIsInstance(all_records, dict) def test_records_get_by_type_mobile(self) -> None: # Given record_type = 'Commits' self.task_set.appian.records.get_all_record_types() # When all_records = self.task_set.appian.records.get_all_records_of_record_type_mobile( record_type) # Then self.assertIsInstance(all_records, dict) def test_records_fetch_record_instance(self) -> None: record = self.task_set.appian.records.fetch_record_instance( "Commits", self.record_instance_name, False) self.assertIsInstance(record, dict) def test_records_fetch_record_instance_no_record_type(self) -> None: with self.assertRaisesRegex( Exception, "There is no record type with name .* in the system under test" ): self.task_set.appian.records.fetch_record_instance( "something else 1", self.record_instance_name, False) def test_records_fetch_record_instance_missing(self) -> None: with self.assertRaisesRegex( Exception, "There is no record with name .* found in record type .*"): self.task_set.appian.records.fetch_record_instance( "Commits", "something else", False) def test_records_fetch_record_type(self) -> None: self.task_set.appian.records.get_all() output = self.task_set.appian.records.fetch_record_type("Commits") self.assertIsInstance(output, dict) def test_records_fetch_record_type_recaching(self) -> None: self.task_set.appian.records._record_types = dict( ) # Resetting the cache. output = self.task_set.appian.records.fetch_record_type("Commits") self.assertIsInstance(output, dict) def test_records_fetch_record_type_missing_record_type(self) -> None: with self.assertRaisesRegex( Exception, "There is no record type with name .* in the system under test" ): self.task_set.appian.records.fetch_record_type("something else") def test_records_visit(self) -> None: output_json, output_uri = self.task_set.appian.records.visit_record_instance( "Commits", self.record_instance_name, exact_match=False) self.assertIsInstance(output_json, dict) self.assertTrue("summary" in output_uri) def test_records_visit_random_success(self) -> None: self.custom_locust.set_default_response(200, self.record_summary_view) output_json, output_uri = self.task_set.appian.records.visit_record_instance( ) self.assertIsInstance(output_json, dict) self.assertTrue("summary" in output_uri) def test_records_visit_random_of_selected_record_type_success( self) -> None: self.custom_locust.set_default_response(200, self.record_summary_view) output_json, output_uri = self.task_set.appian.records.visit_record_instance( record_type="Commits") self.assertIsInstance(output_json, dict) self.assertTrue("summary" in output_uri) def test_records_visit_random_no_record_type_failure(self) -> None: with self.assertRaisesRegex( Exception, "If record_name parameter is specified, record_type must also be included" ): self.task_set.appian.records.visit_record_instance( record_name=self.record_instance_name, exact_match=False) def test_records_visit_with_urlstub(self) -> None: output_json, output_uri = self.task_set.appian.records.visit_record_instance( "Commits", self.record_instance_name, "summary", exact_match=False) self.assertIsInstance(output_json, dict) self.assertTrue("summary" in output_uri) def test_record_types_visit(self) -> None: output_json, output_uri = self.task_set.appian.records.visit_record_type( "Commits", exact_match=False) self.assertIsInstance(output_json, dict) self.assertTrue("commit" in output_uri) def test_record_type_visit_random_success(self) -> None: self.custom_locust.set_default_response(200, self.records) output_json, output_uri = self.task_set.appian.records.visit_record_type( ) self.assertIsInstance(output_json, dict) self.assertEqual(output_json.get("#t"), "UiConfig") self.assertTrue("commit" in output_uri) def test_record_type_visit_failure(self) -> None: with self.assertRaises(Exception) as e: self.task_set.appian.records.visit_record_type( record_type="fake type") @unittest.mock.patch( 'appian_locust.records_helper.find_component_by_attribute_in_dict', return_value={'children': [json.dumps({"a": "b"})]}) def test_records_form_example_success( self, find_component_by_attribute_in_dict_function: Any) -> None: sail_form = self.task_set.appian.records.visit_record_instance_and_get_form( "Commits", self.record_instance_name, view_url_stub="summary", exact_match=False) self.assertTrue(isinstance(sail_form, SailUiForm)) self.assertEqual(sail_form.get_response(), {"a": "b"}) def test_records_form_incorrect_name(self) -> None: record_name = "Fake Record" record_type = "Commits" exact_match = False with self.assertRaises(Exception) as context: self.task_set.appian.records.visit_record_instance_and_get_form( record_type, record_name, view_url_stub="summary", exact_match=exact_match) self.assertEqual( context.exception.args[0], f"There is no record with name {record_name} found in record type {record_type} (Exact match = {exact_match})" ) def test_records_form_incorrect_type(self) -> None: exact_match = False with self.assertRaises(Exception) as context: self.task_set.appian.records.visit_record_instance_and_get_form( "Fake Type", self.record_instance_name, view_url_stub="summary", exact_match=exact_match) self.assertEqual( context.exception.args[0], f"There is no record type with name Fake Type in the system under test (Exact match = {exact_match})" ) @unittest.mock.patch( 'appian_locust.records_helper.find_component_by_attribute_in_dict', return_value={'children': None}) def test_records_form_no_embedded_summary( self, find_component_by_attribute_in_dict_function: Any) -> None: with self.assertRaises(Exception) as context: self.task_set.appian.records.visit_record_instance_and_get_form( record_type="Commits", record_name=self.record_instance_name, view_url_stub="summary", exact_match=False) self.assertEqual( context.exception.args[0], "Parser was not able to find embedded SAIL code within JSON response for the requested Record Instance." ) def test_record_types_form_example_success(self) -> None: sail_form = self.task_set.appian.records.visit_record_type_and_get_form( "Commits", exact_match=False) self.assertTrue(isinstance(sail_form, SailUiForm)) def test_record_type_random_form_example_success(self) -> None: sail_form = self.task_set.appian.records.visit_record_type_and_get_form( exact_match=False) self.assertTrue(isinstance(sail_form, SailUiForm)) def test_record_type_form_incorrect_type(self) -> None: record_type = "Fake Type" exact_match = True with self.assertRaises(Exception) as context: self.task_set.appian.records.visit_record_type_and_get_form( record_type, exact_match) self.assertEqual( context.exception.args[0], f"There is no record type with name {record_type} in the system under test" ) def test_get_random_record(self) -> None: self.task_set.appian.records._record_types = dict() self.task_set.appian.records._records = dict() record_type, record_name = self.task_set.appian.records._get_random_record_instance( ) self.assertEqual(record_type, "Commits") self.assertIn( record_name, list(self.task_set.appian.records._records[record_type].keys())) def test_get_random_record_get_all(self) -> None: mock_get_all = mock.Mock() setattr(self.task_set.appian.records, 'get_all', mock_get_all) with self.assertRaises(Exception): self.task_set.appian.records._get_random_record_instance() mock_get_all.assert_called_once() def test_get_record_instance_feed_Response(self) -> None: sail_form = self.task_set.appian.records.visit_record_instance_and_get_feed_form( "Commits", self.record_instance_name, exact_match=False) self.assertTrue(isinstance(sail_form, SailUiForm)) self.assertEqual( sail_form.state.get("feed", {}).get("title", ""), self.record_instance_name)
class FeatureToggleHelperTest(unittest.TestCase): html_snippet = """ </script><script src="/suite/tempo/ui/sail-client/sites-05d032ca6319b11b6fc9.cache.js?\ appian_environment=sites"> </script><script src="/suite/tempo/ui/sail-client/omnibox-05d032ca6319b11b6fc9.cache.js?\ appian_environment=sites" defer="true"> </script></body></html> """ js_snippet = """ ADERS=exports.LEGACY_FEATURE_FLAGS=exports.DEFAULT_FEATURE_FLAGS=undefined; var RAW_DEFAULT_FEATURE_FLAGS={};var RAW_LEGACY_FEATURE_FLAGS=RAW_DEFAULT_FEATURE_FLAGS&2147483647; var DEFAULT_FEATURE_FLAGS=exports.DEFAULT_FEATURE_FLAGS=RAW_DEFAULT_FEATURE_FLAGS.toString(16); var LEGACY_FEATURE_FLAGS=exports.LEGACY_FEATURE_FLAGS=RAW_LEGACY_FEATURE_FLAGS.toString(16);var """ def setUp(self) -> None: self.custom_locust = CustomLocust(Locust()) parent_task_set = TaskSet(self.custom_locust) setattr(parent_task_set, "host", "") setattr(parent_task_set, "credentials", [["", ""]]) setattr(parent_task_set, "auth", ["", ""]) self.task_set = AppianTaskSet(parent_task_set) self.task_set.host = "" # test_on_start_auth_success is covered here. self.custom_locust.set_response("auth?appian_environment=tempo", 200, '{}') self.task_set.on_start() def tearDown(self) -> None: self.task_set.on_stop() def test_get_javascript_uri_missing(self) -> None: # Given self.custom_locust.set_response("/suite/sites", 200, "abc") # When uri = feature_toggle_helper._get_javascript_uri(self.task_set.appian.interactor, {}) # Then self.assertEqual(uri, None) def test_get_javascript_uri_present(self) -> None: # Given a snippet of the sites page self.custom_locust.set_response("/suite/sites", 200, self.html_snippet) # When uri = feature_toggle_helper._get_javascript_uri(self.task_set.appian.interactor, {}) # Then self.assertEqual( uri, "/suite/tempo/ui/sail-client/sites-05d032ca6319b11b6fc9.cache.js") def test_get_javascript_and_find_feature_flag_missing_value(self) -> None: # Given self.custom_locust.set_response("abc", 200, "body") # When flag = feature_toggle_helper._get_javascript_and_find_feature_flag( self.task_set.appian.client, "abc", {}) # Then self.assertEqual(flag, None) def test_get_javascript_and_find_feature_flag_value_present_hex(self) -> None: # Given a snippet of the minified js self.custom_locust.set_response("/suite/file.js", 200, self.js_snippet.format("0xdc9fffceebc")) # When uri = feature_toggle_helper._get_javascript_and_find_feature_flag(self.task_set.appian.client, "/suite/file.js", {}) # Then self.assertEqual( uri, "0xdc9fffceebc") def test_get_javascript_and_find_feature_flag_value_present_big_int(self) -> None: # Given a snippet of the minified js for i in [9, 10]: self.custom_locust.set_response("/suite/file.js", 200, self.js_snippet.format( f'jsbi__WEBPACK_IMPORTED_MODULE_{i}__["default"].BigInt("0b110100100111011100000111111111111111001110111010111100")')) # When binVal = feature_toggle_helper._get_javascript_and_find_feature_flag(self.task_set.appian.client, "/suite/file.js", {}) # Then self.assertEqual( binVal, "0b110100100111011100000111111111111111001110111010111100") def test_get_javascript_and_find_feature_flag_value_present_integer(self) -> None: # Given a snippet of the minified js self.custom_locust.set_response("/suite/file.js", 200, self.js_snippet.format("5802956083228348")) # When uri = feature_toggle_helper._get_javascript_and_find_feature_flag(self.task_set.appian.client, "/suite/file.js", {}) # Then self.assertEqual( uri, "5802956083228348") def test_get_feature_flags_from_regex_match_hex(self) -> None: # Given hex_val = "0xdc9fffceebc" # When flag, flag_extended = feature_toggle_helper._get_feature_flags_from_regex_match( hex_val) # Then self.assertEqual(flag, "7ffceebc") self.assertEqual(flag_extended, "dc9fffceebc") def test_get_feature_flags_from_regex_match_big_int(self) -> None: # Given hex_val = "0b110100100111011100000111111111111111001110111010111100" # When flag, flag_extended = feature_toggle_helper._get_feature_flags_from_regex_match( hex_val) # Then self.assertEqual(flag, "7ffceebc") self.assertEqual(flag_extended, "349dc1fffceebc") def test_get_feature_flags_from_regex_match_integer(self) -> None: # Given int_val = "5802956083228348" # When flag, flag_extended = feature_toggle_helper._get_feature_flags_from_regex_match( int_val) # Then self.assertEqual(flag, "7ffceebc") self.assertEqual(flag_extended, "149dc1fffceebc") def test_end_to_end_get_feature_flags(self) -> None: # Given a snippet of the sites page and js snippet self.custom_locust.set_response("/suite/sites", 200, self.html_snippet) self.custom_locust.set_response("/suite/tempo/ui/sail-client/sites-05d032ca6319b11b6fc9.cache.js", 200, self.js_snippet.format("5802956083228348")) # When flag, flag_extended = feature_toggle_helper.get_client_feature_toggles( self.task_set.appian.interactor, self.task_set.appian.client ) # Then self.assertEqual(flag, "7ffceebc") self.assertEqual(flag_extended, "149dc1fffceebc") def test_end_to_end_get_feature_flags_fail_no_sites_link(self) -> None: # Given a missing js snippet self.custom_locust.set_response("/suite/sites", 200, '') # When and Then with self.assertRaisesRegex(Exception, "Could not find script uri to retrieve client feature"): feature_toggle_helper.get_client_feature_toggles( self.task_set.appian.interactor, self.task_set.appian.client ) def test_end_to_end_get_feature_flags_fail_no_feature_toggles_found(self) -> None: # Given a missing feature toggle self.custom_locust.set_response("/suite/sites", 200, self.html_snippet) self.custom_locust.set_response("/suite/tempo/ui/sail-client/sites-05d032ca6319b11b6fc9.cache.js", 200, 'missin') # When and Then with self.assertRaisesRegex(Exception, "Could not find flag string within uri /suite/tempo/ui"): feature_toggle_helper.get_client_feature_toggles( self.task_set.appian.interactor, self.task_set.appian.client ) def test_to_hex_str(self) -> None: # Given hex_val = "0xdc9fffceebc" intVal = int(hex_val, 16) # When flagHexStr = feature_toggle_helper._to_hex_str(intVal) # Then self.assertEqual(flagHexStr, "dc9fffceebc") def test_truncate_flag_extended_long_input(self) -> None: # Given longHexVal = 0xdc9fffceebc # When shortVal = feature_toggle_helper._truncate_flag_extended(longHexVal) # Then self.assertEqual(shortVal, 0x7ffceebc) def test_truncate_flag_extended_short_input(self) -> None: # Given shortHexVal = 0x7ffceebc # When shortVal = feature_toggle_helper._truncate_flag_extended(shortHexVal) # Then self.assertEqual(shortVal, 0x7ffceebc) def create_override_flag_mask_runner(self, flags: List[FeatureFlag], expectedResult: int) -> None: # When feature_flag_mask = feature_toggle_helper._create_override_flag_mask(flags) # Then self.assertEqual(feature_flag_mask, expectedResult) def test_create_override_flag_mask_no_flag(self) -> None: # Given flags: List[FeatureFlag] = [] expectedResult = 0 # Then self.create_override_flag_mask_runner(flags, expectedResult) def test_create_override_flag_mask_one_flag(self) -> None: # Given flags = [FeatureFlag.SAIL_FORMS] expectedResult = 4 # Then self.create_override_flag_mask_runner(flags, expectedResult) def test_create_override_flag_mask_multiple_flags(self) -> None: # Given flags = [FeatureFlag.SAIL_FORMS, FeatureFlag.COMPACT_URI_TEMPLATES] expectedResult = 1028 # Then self.create_override_flag_mask_runner(flags, expectedResult) def override_default_flags_runner(self, flags: List[FeatureFlag], expected_featured_flag_extended: str, expected_feature_flag: str) -> None: # Given a snippet of the sites page self.task_set.appian.interactor.client.feature_flag_extended = "149dc1fffceebc" self.task_set.appian.interactor.client.feature_flag = "7ffceebc" # When feature_toggle_helper.override_default_flags(self.task_set.appian.interactor, flags) # Then self.assertEqual(self.task_set.appian.interactor.client.feature_flag_extended, expected_featured_flag_extended) self.assertEqual(self.task_set.appian.interactor.client.feature_flag, expected_feature_flag) def test_override_default_flags_no_flags(self) -> None: # Given a snippet of the sites page flags: List[FeatureFlag] = [] self.override_default_flags_runner(flags, "149dc1fffceebc", "7ffceebc") def test_override_default_flags_one_flag(self) -> None: # Given a snippet of the sites page flags = [FeatureFlag.SHORT_CIRCUIT_PARTIAL_RENDERING] self.override_default_flags_runner(flags, "149dc1fffcefbc", "7ffcefbc") def test_override_default_flags_multiple_flags(self) -> None: # Given a snippet of the sites page flags = [FeatureFlag.SHORT_CIRCUIT_PARTIAL_RENDERING, FeatureFlag.INLINE_TASK_CONTROLS] self.override_default_flags_runner(flags, "149dc1fffcffbc", "7ffcffbc") def test_declare_device_as_mobile(self) -> None: # Given a snippet of the sites page self.custom_locust.set_response("/suite/sites", 200, self.html_snippet) flags = [FeatureFlag.RECORD_LIST_FEED_ITEM_DTO] self.override_default_flags_runner(flags, "149dd1fffceebc", "7ffceebc")