def step_generic_modify_request_json(ctx: Context, target_key: str, value: str) -> None: """Modifies the request data in the behave context to have the actual value of the given string representation. Args: ctx: The behave context target_key: The key whose value is to be modified value: A string representation of what the new value should be """ LOGGER.debug( f"Attempting to modify the JSON request payload at: {target_key} to be: {value}." ) desired_value = CommonBehave.interpolate_context_attributes(ctx, value) parsed_key = CommonBehave.interpolate_context_attributes(ctx, target_key) if desired_value.lower() in ("none", "null"): ctx.request_data = modify_json_value(ctx.request_data, parsed_key, None) elif desired_value.lower() in ("invalid", "empty", "empty string"): ctx.request_data = modify_json_value(ctx.request_data, parsed_key, "") elif desired_value.lower() in "uuid": ctx.request_data = modify_json_value(ctx.request_data, parsed_key, str(uuid.uuid4())) else: ctx.request_data = modify_json_value(ctx.request_data, parsed_key, desired_value) LOGGER.debug("Successfully updated JSON request payload.")
def step_debug_pdb(ctx: Context) -> None: """Sets a pdb to be used for debugging. The gherkin-linter will catch this step to prevent a pdb being pushed. Multiple if statements log everything a dev would need to start debugging the issue. Args: ctx: The behave context object """ LOGGER.debug( "Behave PDB step was set. Current context attributes broken down by context layer:" ) CommonBehave.log_context_attributes(ctx) pdb.set_trace()
def step_assert_headers(ctx: Context, negate: str) -> None: """Checks that the headers have the proper values. Args: ctx: The behave context negate: A string representing whether or not a response should be negated. If it should be negated, it will have a value 'not'. Otherwise, it will be None """ for row in ctx.table: header = ctx.response.headers.get(row[0]) header_value = CommonBehave.interpolate_context_attributes(ctx, row[1]) if negate: LOGGER.debug( f"Checking that the response header: {row[0]} should not be: {header_value}." ) assert ( header != header_value ), f'Expected response header: "{row[0]}" to not be "{header_value}", but it was "{header}"' else: LOGGER.debug( f"Checking that the response header: {row[0]} should be: {header_value}." ) assert ( header == header_value ), f'Expected response header: "{row[0]}" to be "{header_value}", but it was "{header}"' LOGGER.debug("Successfully validated response headers.")
def step_save_response_attribute_to_context(ctx: Context, key: str, value_name: str) -> None: """Sets the JSON response with a given key to a value in the behave context with an attribute name of <value_name> Args: ctx: The behave context key: The key from the JSON response value_name: The new name for the new attribute in the behave context """ interpolated_key = CommonBehave.interpolate_context_attributes( ctx, f"$.{key}") list_of_values = jsonpath(ctx.response.json(), interpolated_key) LOGGER.debug( f"Attempting to save JSON response at: {interpolated_key} as: {value_name}." ) if list_of_values: found_value = list_of_values[0] setattr(ctx, value_name, found_value) LOGGER.debug( f"Successfully saved response attribute: {key} as {value_name}.") else: raise KeyError( f"No key: '{interpolated_key}' found in the JSON response: {ctx.response.json()}" )
def step_assert_rest_response_collection_size(ctx: Context, key: str, negate: str, collection_size: str) -> None: """Checks that the JSON response does or does not have a collection of the given size. Args: ctx: The behave context negate: A string representing whether or not a response should be negated. If it should be negated, it will have a value 'not'. Otherwise, it will be None key: The key in the JSON response where the collection size should be checked collection_size: The expected size of the collection """ if key: list_of_values = jsonpath( ctx.response.json(), CommonBehave.interpolate_context_attributes(ctx, key)) if list_of_values: found_value = list_of_values[0] if isinstance(found_value, (dict, list)): if negate: LOGGER.debug( f"Checking that the JSON response at key: {key} should not have size: {collection_size}." ) assert len(found_value) != int(collection_size), ( f"Expected the JSON value at key: {key} to not be a collection with size: {int(collection_size)}," f" but we found: {len(found_value)} in the collection") LOGGER.debug( f"Validated that the JSON response at key: {key} did not have size: {collection_size}." ) else: LOGGER.debug( f"Checking that the JSON response at key: {key} should have size: {collection_size}." ) assert len(found_value) == int(collection_size), ( f"Expected the JSON value at key: {key} to be a collection with size: {int(collection_size)}," f" but we found: {len(found_value)} in the collection") LOGGER.debug( f"Validated that the JSON response at key: {key} did have size: {collection_size}." ) else: raise TypeError( f"No dict or list found at key: '{key}'. We found: {type(found_value).__name__}" ) else: raise KeyError( f"No key: '{key}' found in the JSON response: {ctx.response.json()}" ) else: LOGGER.debug( f"Checking that the JSON response should have size: {collection_size}." ) found_value = len(ctx.response.json()) assert found_value == int(collection_size), ( f"Expected the JSON value to be a collection with size: {int(collection_size)}," f" but we found: {found_value} in the collection") LOGGER.debug( f"Validated that the JSON response did have size: {collection_size}." )
def step_assert_rest_response_key(ctx: Context, negate: str, key: str) -> None: """Checks that the JSON response contains a given key. Args: ctx: The behave context negate: A string representing whether or not a response should be negated. If it should be negated, it will have a value 'not'. Otherwise, it will be None key: The key that should be present in the response """ list_of_values = jsonpath( ctx.response.json(), CommonBehave.interpolate_context_attributes(ctx, f"$.{key}"), ) if negate: LOGGER.debug( f"Checking that the JSON response should not include key: {key}.") assert ( not list_of_values ), f"Expected no key to be found at '{key}', but it was present. Response: {ctx.response.text}" LOGGER.debug( f"Validated that the JSON response did not include key: {key}.") else: LOGGER.debug( f"Checking that the JSON response should include key: {key}.") assert ( list_of_values ), f"Expected key to be found at '{key}', but it was not present. Response: {ctx.response.text}" LOGGER.debug( f"Validated that the JSON response did include key: {key}.")
def step_assert_rest_response_value(ctx: Context, key: str, negate: str, value: str) -> None: """Generic step definition that will read in a key using json path and traverse the json to find the value. Assertions are done based on the negate flag. Json path is a language that allows you to find values in json based on an expression. Find more info on json path here: https://restfulapi.net/json-jsonpath/. Args: ctx: behave context negate: type of assert. Either should be or should not be. represented by optional gherkin syntax = 'no' key: json path key expression value: the expected value in the assert """ list_of_values = jsonpath( ctx.response.json(), CommonBehave.interpolate_context_attributes(ctx, key)) desired_value = CommonBehave.interpolate_context_attributes(ctx, value) if list_of_values: found_value = str(list_of_values[0]) if negate: LOGGER.debug( f"Checking that the JSON response at key: {key} should not be: {value}." ) assert ( found_value != desired_value ), f"Expected the JSON value at key: {key} to not be: {desired_value}, but we found: {found_value}" LOGGER.debug( f"Validated that the JSON response at key: {key} was not value: {value}" ) else: LOGGER.debug( f"Checking that the JSON response at key: {key} should be: {value}." ) assert ( found_value == desired_value ), f"Expected the JSON value at key: {key} to be: {desired_value}, but we found: {found_value}" LOGGER.debug( f"Validated that the JSON response at key: {key} was value: {value}" ) else: raise KeyError( f"No key: '{key}' found in the JSON response: {ctx.response.json()}" )
def step_save_position_attribute_to_context(ctx: Context, key: str, value: str, json_key: str = None) -> None: """ Attempts to obtain a key from the JSON response and set the value of that key to the behave context Args: ctx: The behave context key: The positional key that is being searched for value: The value the positional key should have json_key: The json key that indicates where the search for the positional key should occur """ interpolated_key = CommonBehave.interpolate_context_attributes(ctx, key) interpolated_value = CommonBehave.interpolate_context_attributes( ctx, value) # Check if we are at the root of the json. If we are then use the '.' instead of the json key if json_key: list_of_values = jsonpath(ctx.response.json(), json_key) else: list_of_values = jsonpath(ctx.response.json(), ".") LOGGER.debug( f"Attempting to find position where key: {key} is value: {value} at: {list_of_values}." ) if list_of_values: json_objects = list_of_values[0] for json_object in json_objects: if json_object[interpolated_key] == interpolated_value: setattr(ctx, "position", str(json_objects.index(json_object))) LOGGER.debug( f"Successfully saved position as: {ctx.position} for key: {key} and value {value} at {list_of_values}." ) break else: raise KeyError( f"No key: '{interpolated_key}' found in the dictionary: {json_object}" ) else: raise KeyError( f"No collection found at '{json_key}' in the JSON response: {ctx.response.json()}" )
def step_url_navigation(ctx: Context, endpoint: str) -> None: """ Navigate to a page by it's url Args: ctx: The behave context endpoint: The url of the page without the base host name """ LOGGER.debug(f"Attempting to navigate to {ctx.host}{endpoint}") endpoint = CommonBehave.interpolate_context_attributes(ctx, endpoint) ctx.driver.get(f"{ctx.host}{endpoint}") LOGGER.debug(f"Successfully navigated to {ctx.host}{endpoint}")
def step_assert_rest_response_data_type(ctx: Context, key: str, negate: str, data_type: str) -> None: """Checks that the key in the response is of the given data type. Args: ctx: The behave context key: The key in the response negate: A string representing whether or not a response should be negated. If it should be negated, it will have a value 'not'. Otherwise, it will be None data_type: The data type of the key """ list_of_values = jsonpath( ctx.response.json(), CommonBehave.interpolate_context_attributes(ctx, f"$.{key}"), ) if list_of_values: found_value = list_of_values[0] class_name = found_value.__class__.__name__ if negate: LOGGER.debug( f"Checking that the JSON response at key: {key} should not have data type: {data_type}." ) assert ( class_name != data_type ), f"Expected the data type at key: '{key}' to not be '{data_type}', but we found: '{class_name}'" LOGGER.debug( f"Validated that the JSON response at key: {key} did not have data type: {data_type}." ) else: LOGGER.debug( f"Checking that the JSON response at key: {key} should have data type: {data_type}." ) assert ( class_name == data_type ), f"Expected the data type at key: '{key}' to be '{data_type}', but we found: '{class_name}'" LOGGER.debug( f"Validated that the JSON response at key: {key} did have data type: {data_type}." ) else: raise KeyError( f"No key: '{key}' found in the JSON response: {ctx.response.json()}" )
def step_generic_json_dictionary(ctx: Context, value_name: str = None) -> None: """Sets the given table representing a request payload to an attribute in the behave context as a given name. Args: ctx: The behave context value_name: The name of the attribute in the behave context """ LOGGER.debug(f"Attempting to save payload as: {value_name}.") payload: Dict[str, Union[str, int]] = {} for row in ctx.table: value = CommonBehave.interpolate_context_attributes(ctx, row[1]) # If value is a digit .. cast to int if value.isdigit(): payload[row[0]] = int(value) else: payload[row[0]] = value # If value_name is specified, set a context attribute with that name equal to the payload. # Else set ctx.request_data equal to the payload. if value_name: setattr(ctx, value_name, payload) else: ctx.request_data = payload LOGGER.debug(f"Successfully saved payload: {payload} as: {value_name}.")
def step_set_request_header(ctx: Context, header: str, header_value: str) -> None: """Sets a given type of request header to a given value. Args: ctx: The behave context header: The type of request header header_value: The value the request header should have """ ctx.headers = {} desired_value = CommonBehave.interpolate_context_attributes( ctx, header_value) LOGGER.debug( f"Attempting to set request header: {header} to: {header_value}.") if desired_value.lower() in ("invalid", "empty", "empty string"): ctx.headers[header] = "" elif desired_value.lower() in ("none", "null"): ctx.headers[header] = None else: ctx.headers[header] = desired_value LOGGER.debug( f"Successfully set request header: {header} to: {ctx.headers[header]}." )