From 0f8b284c91647aa0f770e86cb046ca1ad879d388 Mon Sep 17 00:00:00 2001 From: Sheldon Regular Date: Tue, 7 Nov 2023 14:31:53 -0500 Subject: [PATCH 1/3] initial showcase test structure Signed-off-by: Sheldon Regular --- .../bc_showcase_issuer_agent_interface.py | 124 ++++++++++++++ .../bc_showcase_verifier_agent_interface.py | 160 ++++++++++++++++++ .../features/bc_wallet/bc_showcase.feature | 37 ++++ 3 files changed, 321 insertions(+) create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_verifier_agent_interface.py create mode 100644 aries-mobile-tests/features/bc_wallet/bc_showcase.feature diff --git a/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py new file mode 100644 index 0000000..b904638 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py @@ -0,0 +1,124 @@ +""" +Class for BC Showcase issuer agent for Student and Lawer Showcase Credentials +""" +import base64 +from agent_factory.issuer_agent_interface import IssuerAgentInterface +from sys import platform +from selenium import webdriver +from selenium.webdriver.chrome.service import Service +from selenium.webdriver.chrome.options import Options +from webdriver_manager.chrome import ChromeDriverManager +from webdriver_manager.core.os_manager import ChromeType +# import Page Objects needed +from agent_factory.candy_uvp.pageobjects.terms_of_service_page import TermsOfServicePage +from agent_factory.candy_uvp.pageobjects.request_credential_page import RequestCredentialPage +from agent_factory.candy_uvp.pageobjects.review_and_confirm_page import ReviewAndConfirmPage +from agent_factory.candy_uvp.pageobjects.connect_with_issuer_page import ConnectWithIssuerPage +from agent_factory.candy_uvp.pageobjects.issuing_credential_page import IssuingCredentialPage + + +class BCShowcaseIssuerAgentInterface(IssuerAgentInterface): + + _terms_of_service_page: TermsOfServicePage + _request_credential_page: RequestCredentialPage + _review_and_confirm_page: ReviewAndConfirmPage + _connect_with_issuer_page: ConnectWithIssuerPage + _issuing_credential_page: IssuingCredentialPage + + # Default schema and cred + DEFAULT_CREDENTIAL_DATA = { + "first_name": "firstname", + "last_name": "lastname", + "date_of_birth": "1968-06-22", + "street_address": "1968 oh six twenty two street", + "postal_code": "K7P 2N3", + "city": "Victoria", + "province": "British Columbia", + } + + def __init__(self, endpoint): + # Standup Selenuim Driver with endpoint + super().__init__(endpoint) + if platform == "linux" or platform == "linux2": + print("Starting Chromium on linux for BC Showcase Issuer Agent") + options = Options() + options.add_argument("--no-sandbox") + options.add_argument("--disable-dev-shm-usage") + options.add_argument("--headless") + self.driver = webdriver.Chrome(options=options, service=Service(ChromeDriverManager(chrome_type=ChromeType.CHROMIUM).install())) + else: + print("Starting Chrome on Mac or Windows for Issuer Agent") + self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) + # go to the issuer endpoint in the browser + self.driver.get(self.endpoint) + # instantiate intial page objects + self._terms_of_service_page = TermsOfServicePage(self.driver) + # make sure we are on the first page, the terms of service page + if not self._terms_of_service_page.on_this_page(): + raise Exception('Something is wrong, not on the Terms of Service Page for the CANdy UVP Issuer') + + def get_issuer_type(self) -> str: + """return the type of issuer as a string CANdyUVPIssuer""" + return "CANdyUVPIssuer" + + def create_invitation(self, oob=False, print_qrcode=False, save_qrcode=False, qr_code_border=40): + # This is not supported on CANdy UVP Issuer. Connection is made when creating the credential + # If called, send an exception back on this one and let the test handle it. Maybe a IssuerInterfaceFunctionNotSupported error. + return Exception('Function not supported for CANdy UVP Issuer') + + def connected(self): + """return true if connected""" + # Check Issuing Credential Page for "Connected to the Issuer Agent" + return self._issuing_credential_page.connected() + + + def send_credential(self, version=1, schema=None, credential_offer=None, revokable=False): + """send a credential to the holder, returns a qr code for holder to connect to""" + self._terms_of_service_page.select_i_agree() + self._request_credential_page = self._terms_of_service_page.agree() + + if credential_offer: + # Make credential_offer format into name value pairs + credential_data = self._create_name_value_pairs_from_credential_offer(credential_offer) + self._request_credential_page.enter_first_name(credential_data["first_name"]) + self._request_credential_page.enter_last_name(credential_data["last_name"]) + self._request_credential_page.enter_dob(credential_data["date_of_birth"]) + self._request_credential_page.enter_street_address(credential_data["street_address"]) + self._request_credential_page.enter_postal_code(credential_data["postal_code"]) + self._request_credential_page.enter_city(credential_data["city"]) + self._request_credential_page.enter_province(credential_data["province"]) + else: + self._request_credential_page.enter_first_name(self.DEFAULT_CREDENTIAL_DATA["first_name"]) + self._request_credential_page.enter_last_name(self.DEFAULT_CREDENTIAL_DATA["last_name"]) + self._request_credential_page.enter_dob(self.DEFAULT_CREDENTIAL_DATA["date_of_birth"]) + self._request_credential_page.enter_street_address(self.DEFAULT_CREDENTIAL_DATA["street_address"]) + self._request_credential_page.enter_postal_code(self.DEFAULT_CREDENTIAL_DATA["postal_code"]) + self._request_credential_page.enter_city(self.DEFAULT_CREDENTIAL_DATA["city"]) + self._request_credential_page.enter_province(self.DEFAULT_CREDENTIAL_DATA["province"]) + + self._review_and_confirm_page = self._request_credential_page.request_credential() + + self._review_and_confirm_page.select_i_confirm() + self._connect_with_issuer_page = self._review_and_confirm_page.proceed() + + qrcode = self._connect_with_issuer_page.get_qr_code() + self._issuing_credential_page = IssuingCredentialPage(self.driver) + return qrcode + + + def restart_issue_credential(self): + # go to the issuer endpoint in the browser + self.driver.get(self.endpoint) + # make sure we are on the first page, the terms of service page + if not self._terms_of_service_page.on_this_page(): + raise Exception('Something is wrong, not on the Terms of Service Page for the CANdy UVP Issuer') + + def revoke_credential(self, publish_immediately=True, notify_holder=False): + """revoke a credential""" + return Exception('Function not supported for CANdy UVP Issuer') + + def _create_name_value_pairs_from_credential_offer(self, credential_offer): + credential_data = {} + for attribute in credential_offer["attributes"]: + credential_data[attribute["name"]] = attribute["value"] + return credential_data \ No newline at end of file diff --git a/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_verifier_agent_interface.py b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_verifier_agent_interface.py new file mode 100644 index 0000000..51d9564 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_verifier_agent_interface.py @@ -0,0 +1,160 @@ +""" +Absctact Base Class for actual verifier agent interfaces to implement +""" + +from asyncio import sleep +from agent_factory.verifier_agent_interface import VerifierAgentInterface +import json +from agent_test_utils import get_qr_code_from_invitation +from agent_controller_client import agent_controller_GET, agent_controller_POST, expected_agent_state, setup_already_connected + + +class BCShowcaseVerifierAgentInterface(VerifierAgentInterface): + + # Default schema and cred + DEFAULT_PROOF_REQUEST = { + "requested_attributes": { + "Person":{ + "restrictions":[ + { + "schema_name":"Person" + } + ], + "names":[ + "picture", + "family_name", + "given_names", + "locality", + "region", + "postal_code", + "street_address", + "country", + "expiry_date_dateint", + "birthdate_dateint" + ] + } + }, + "requested_predicates":{ + + }, + "version":"1.0.0", + "name":"BC Digital ID Request" + } + + def get_issuer_type(self) -> str: + """return the type of issuer as a string BCPersonShowcaseVerifier""" + return "BCPersonShowcaseVerifier" + + def create_invitation(self, oob=False, print_qrcode=False, save_qrcode=False, qr_code_border=40): + """create an invitation and return the json back to the caller """ + # https://bc-wallet-demo-agent-admin-test.apps.silver.devops.gov.bc.ca/connections/create-invitation + + self._oob = oob + if self._oob is True: + data = {"use_public_did": False} + (resp_status, resp_text) = agent_controller_POST( + self.endpoint + "/agent/command/", + "out-of-band", + operation="send-invitation-message", + data=data, + ) + else: + (resp_status, resp_text) = agent_controller_POST( + self.endpoint, topic="/connections", operation="create-invitation" + ) + + if resp_status != 200: + raise Exception( + f"Call to create connection invitation failed: {resp_status}; {resp_text}" + ) + else: + self.invitation_json = json.loads(resp_text) + qrimage = get_qr_code_from_invitation(self.invitation_json, print_qrcode, save_qrcode, qr_code_border) + return qrimage + + def connected(self): + """return True/False indicating if this issuer is connected to the wallet holder """ + + # If OOB then make a call to get the connection id from the webhook. + if self._oob == True: + # Get the responders's connection id from the above request's response webhook in the backchannel + invitation_id = self.invitation_json["invitation"]["@id"] + (resp_status, resp_text) = agent_controller_GET( + self.endpoint + "/agent/response/", "did-exchange", id=invitation_id + ) + if resp_status != 200: + raise Exception( + f"Call get the connection id from the OOB connection failed: {resp_status}; {resp_text}" + ) + else: + resp_json = json.loads(resp_text) + connection_id = resp_json["connection_id"] + self.invitation_json["connection_id"] = connection_id + else: + connection_id = self.invitation_json['connection']['id'] + + return self._expected_connection_state(self.endpoint, "/connections", connection_id, "complete", 6, 2) + + + def _expected_connection_state(self, agent_url, protocol_txt, id, status_txt, wait_time=2.0, sleep_time=0.5): + sleep(sleep_time) + state = "None" + if type(status_txt) != list: + status_txt = [status_txt] + # "N/A" means that the controller can't determine the state - we'll treat this as a successful response + status_txt.append("N/A") + #for i in range(int(wait_time/sleep_time)): + for i in range(int(wait_time)): + (resp_status, resp_text) = agent_controller_GET(agent_url, protocol_txt, id=id) + if resp_status == 200: + resp_json = json.loads(resp_text) + state = resp_json["state"] + if state in status_txt: + return True + sleep(sleep_time) + + print("From", agent_url, "Expected state", status_txt, "but received", state, ", with a response status of", resp_status) + return False + + def send_proof_request(self, version=1, request_for_proof=None, connectionless=False): + """create a proof request """ + + if version == 2: + topic = "proof-v2" + else: + topic = "/proofs" + + if request_for_proof: + pass + # if context.non_revoked_timeframe: + # data["non_revoked"] = context.non_revoked_timeframe["non_revoked"] + else: + request_for_proof = self.DEFAULT_PROOF_REQUEST.copy() + + presentation_request = { + "connectionId": self.invitation_json['connection']['id'], + "proofRequest": request_for_proof, + "comment": f"proof request from {self.get_issuer_type()} {self.endpoint}" + } + + if connectionless: + operation = "create-send-connectionless-request" + else: + #presentation_request["connectionId"] = self.invitation_json['connection']['id'] + operation = "request-proof" + + (resp_status, resp_text) = agent_controller_POST( + self.endpoint, + topic, + operation=operation, + data=presentation_request, + wrap_data_with_data=False + ) + if resp_status != 200: + raise Exception( + f"Call to send proof request failed: {resp_status}; {resp_text}" + ) + else: + self.proof_request_json = json.loads(resp_text) + + diff --git a/aries-mobile-tests/features/bc_wallet/bc_showcase.feature b/aries-mobile-tests/features/bc_wallet/bc_showcase.feature new file mode 100644 index 0000000..470a0e1 --- /dev/null +++ b/aries-mobile-tests/features/bc_wallet/bc_showcase.feature @@ -0,0 +1,37 @@ +@BCShowcase @bc_wallet +Feature: BC Showcase + In order to easily show the capabilities of BC Wallet and ensure those services are working + As a BC Wallet Stakeholder + I want to be able to run nightly tests on the BC Showcase with BC Wallet + + + @T001-BCShowcase @critical @AcceptanceTest + Scenario Outline: BC Showcase Student gets access to a store discount + Given a wallet user + | pin | biometrics | + | 369369 | off | + + When the has a from + When the Student has credentials + | credential | revocable | issuer_agent_type | credential_name | + | N/A | False | BCShowcaseBestBCCollegeIssuer | Student Card | + + And the has a proof request of from + And the Student has a proof request + | verifier_agent_type | proof_request | + | BCShowcaseBestBCCollegeVerifier | Student Card | + + And they select Share + + Then they have + Then they have Access + | proof_result | + | Room Booked | + + Examples: + | user | issuer_agent_type | credentials | verifier_agent_type | proof_request | proof_result | + | Student | BCShowcaseBestBCCollegeIssuer | Student Card | BCShowcaseBestBCCollegeVerifier | Student Card | Room Booked | + | Student | BCShowcaseBestBCCollegeIssuer | Student Card | BCShowcaseCoolClothesOnlineVerifier | Student Card | Discount | + | Lawyer | BCShowcaseLSBCIssuer | LSBC Member Card:Person | BCShowcaseCourtServicesBranchVerifier | Member Card & Person | Court Services | + +BCShowcaseServiceBCIssuer Person \ No newline at end of file From 6929ea1aa51ec9fbcf137d179cab8d3fe08c76ef Mon Sep 17 00:00:00 2001 From: Sheldon Regular Date: Wed, 15 Nov 2023 11:18:49 -0500 Subject: [PATCH 2/3] updated tests for BCW with Showcase Signed-off-by: Sheldon Regular --- .../agent_factory/agent_interface_factory.py | 4 +- .../bc_showcase_issuer_agent_interface.py | 99 ++++++++----------- .../bc_wallet_showcase_main_page.py | 27 +++++ .../connect_with_best_bc_college_page.py | 45 +++++++++ .../pageobjects/install_bc_wallet_page.py | 30 ++++++ .../pageobjects/lets_get_started_page.py | 30 ++++++ .../pageobjects/who_do_you_want_to_be_page.py | 50 ++++++++++ .../features/bc_wallet/bc_showcase.feature | 19 ++-- .../features/steps/bc_wallet/bc_showcase.py | 53 ++++++++++ .../features/steps/bc_wallet/wallet_naming.py | 8 +- 10 files changed, 295 insertions(+), 70 deletions(-) create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/bc_wallet_showcase_main_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/connect_with_best_bc_college_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/install_bc_wallet_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/lets_get_started_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/who_do_you_want_to_be_page.py create mode 100644 aries-mobile-tests/features/steps/bc_wallet/bc_showcase.py diff --git a/aries-mobile-tests/agent_factory/agent_interface_factory.py b/aries-mobile-tests/agent_factory/agent_interface_factory.py index 8e360a9..fba0b11 100644 --- a/aries-mobile-tests/agent_factory/agent_interface_factory.py +++ b/aries-mobile-tests/agent_factory/agent_interface_factory.py @@ -2,6 +2,7 @@ Factory class to create agent interface objects given the agent type passed in. """ +from agent_factory.bc_showcase.bc_showcase_issuer_agent_interface import BCShowcaseIssuerAgentInterface from agent_factory.issuer_agent_interface import IssuerAgentInterface from agent_factory.verifier_agent_interface import VerifierAgentInterface from agent_factory.aath.aath_issuer_agent_interface import AATHIssuerAgentInterface @@ -16,7 +17,8 @@ class AgentInterfaceFactory(): issuer_agent_type_interface_dict = { "AATH": AATHIssuerAgentInterface, "CANdy_UVP": CANdy_UVP_IssuerAgentInterface, - "BC_VP": BC_VP_IssuerAgentInterface + "BC_VP": BC_VP_IssuerAgentInterface, + "BCShowcaseIssuer": BCShowcaseIssuerAgentInterface } verifier_agent_type_interface_dict = { "AATH": AATHVerifierAgentInterface, diff --git a/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py index b904638..a5d63d2 100644 --- a/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py +++ b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py @@ -10,31 +10,32 @@ from webdriver_manager.chrome import ChromeDriverManager from webdriver_manager.core.os_manager import ChromeType # import Page Objects needed -from agent_factory.candy_uvp.pageobjects.terms_of_service_page import TermsOfServicePage -from agent_factory.candy_uvp.pageobjects.request_credential_page import RequestCredentialPage -from agent_factory.candy_uvp.pageobjects.review_and_confirm_page import ReviewAndConfirmPage -from agent_factory.candy_uvp.pageobjects.connect_with_issuer_page import ConnectWithIssuerPage -from agent_factory.candy_uvp.pageobjects.issuing_credential_page import IssuingCredentialPage +from agent_factory.bc_showcase.pageobjects.bc_wallet_showcase_main_page import BCWalletShowcaseMainPage +from agent_factory.bc_showcase.pageobjects.who_do_you_want_to_be_page import WhoDoYouWantToBePage +from agent_factory.bc_showcase.pageobjects.lets_get_started_page import LetsGetStartedPage +#from agent_factory.bc_showcase.pageobjects.going_digital_page import GoingDigitalPage +# Lawyer Showcase Page Objects +#from agent_factory.bc_showcase.pageobjects.accessing_court_materials_page import AccessingCourtMaterialsPage +#from agent_factory.bc_showcase.pageobjects.get_your_lawyer_credential_page import GetYourLawyerCredentialPage + +# Student Showcase Page Objects +from agent_factory.bc_showcase.pageobjects.install_bc_wallet_page import InstallBCWalletPage +#from agent_factory.bc_showcase.pageobjects.connect_withbest_bc_college_page import ConnectWithBestBCCollegePage -class BCShowcaseIssuerAgentInterface(IssuerAgentInterface): - _terms_of_service_page: TermsOfServicePage - _request_credential_page: RequestCredentialPage - _review_and_confirm_page: ReviewAndConfirmPage - _connect_with_issuer_page: ConnectWithIssuerPage - _issuing_credential_page: IssuingCredentialPage +class BCShowcaseIssuerAgentInterface(IssuerAgentInterface): - # Default schema and cred - DEFAULT_CREDENTIAL_DATA = { - "first_name": "firstname", - "last_name": "lastname", - "date_of_birth": "1968-06-22", - "street_address": "1968 oh six twenty two street", - "postal_code": "K7P 2N3", - "city": "Victoria", - "province": "British Columbia", - } + _actor : str + _bc_wallet_showcase_main_page: BCWalletShowcaseMainPage + _who_do_you_want_to_be_page: WhoDoYouWantToBePage + _lets_get_started_page: LetsGetStartedPage + _install_bc_wallet_page: InstallBCWalletPage + #_going_digital_page: GoingDigitalPage + #_accessing_court_materials_page: AccessingCourtMaterialsPage + #_get_your_lawyer_credential_page: GetYourLawyerCredentialPage + _install_bc_wallet_page: InstallBCWalletPage + #_connect_with_best_bc_college_page: ConnectWithBestBCCollegePage def __init__(self, endpoint): # Standup Selenuim Driver with endpoint @@ -52,57 +53,39 @@ def __init__(self, endpoint): # go to the issuer endpoint in the browser self.driver.get(self.endpoint) # instantiate intial page objects - self._terms_of_service_page = TermsOfServicePage(self.driver) + self._bc_wallet_showcase_main_page = BCWalletShowcaseMainPage(self.driver) # make sure we are on the first page, the terms of service page - if not self._terms_of_service_page.on_this_page(): - raise Exception('Something is wrong, not on the Terms of Service Page for the CANdy UVP Issuer') + if not self._bc_wallet_showcase_main_page.on_this_page(): + raise Exception('Something is wrong, not on the BC Wallet Showcase Main Page') def get_issuer_type(self) -> str: - """return the type of issuer as a string CANdyUVPIssuer""" - return "CANdyUVPIssuer" + """return the type of issuer as a string BCShowcaseIssuer""" + return "BCShowcaseIssuer" def create_invitation(self, oob=False, print_qrcode=False, save_qrcode=False, qr_code_border=40): - # This is not supported on CANdy UVP Issuer. Connection is made when creating the credential + # This is not supported on the BC Showcase Issuer. Connection is made when sending the credential # If called, send an exception back on this one and let the test handle it. Maybe a IssuerInterfaceFunctionNotSupported error. - return Exception('Function not supported for CANdy UVP Issuer') + return Exception('Function not supported for BC Showcase Issuer') def connected(self): """return true if connected""" # Check Issuing Credential Page for "Connected to the Issuer Agent" - return self._issuing_credential_page.connected() + return self._connect_with_best_bc_college_page.connected() - def send_credential(self, version=1, schema=None, credential_offer=None, revokable=False): + def send_credential(self, actor:str, credential_offer=None, version=1, schema=None, revokable=False): """send a credential to the holder, returns a qr code for holder to connect to""" - self._terms_of_service_page.select_i_agree() - self._request_credential_page = self._terms_of_service_page.agree() - - if credential_offer: - # Make credential_offer format into name value pairs - credential_data = self._create_name_value_pairs_from_credential_offer(credential_offer) - self._request_credential_page.enter_first_name(credential_data["first_name"]) - self._request_credential_page.enter_last_name(credential_data["last_name"]) - self._request_credential_page.enter_dob(credential_data["date_of_birth"]) - self._request_credential_page.enter_street_address(credential_data["street_address"]) - self._request_credential_page.enter_postal_code(credential_data["postal_code"]) - self._request_credential_page.enter_city(credential_data["city"]) - self._request_credential_page.enter_province(credential_data["province"]) + self._who_do_you_want_to_be_page = self._bc_wallet_showcase_main_page.select_get_started() + if actor == "Student": + self._who_do_you_want_to_be_page.select_student() + elif actor == "Lawyer": + self._who_do_you_want_to_be_page.select_lawyer() else: - self._request_credential_page.enter_first_name(self.DEFAULT_CREDENTIAL_DATA["first_name"]) - self._request_credential_page.enter_last_name(self.DEFAULT_CREDENTIAL_DATA["last_name"]) - self._request_credential_page.enter_dob(self.DEFAULT_CREDENTIAL_DATA["date_of_birth"]) - self._request_credential_page.enter_street_address(self.DEFAULT_CREDENTIAL_DATA["street_address"]) - self._request_credential_page.enter_postal_code(self.DEFAULT_CREDENTIAL_DATA["postal_code"]) - self._request_credential_page.enter_city(self.DEFAULT_CREDENTIAL_DATA["city"]) - self._request_credential_page.enter_province(self.DEFAULT_CREDENTIAL_DATA["province"]) - - self._review_and_confirm_page = self._request_credential_page.request_credential() - - self._review_and_confirm_page.select_i_confirm() - self._connect_with_issuer_page = self._review_and_confirm_page.proceed() - - qrcode = self._connect_with_issuer_page.get_qr_code() - self._issuing_credential_page = IssuingCredentialPage(self.driver) + raise Exception(f"Unknown actor type {actor}") + self._lets_get_started_page = self._who_do_you_want_to_be_page.select_next() + self._install_bc_wallet_page = self._lets_get_started_page.select_next() + self._connect_with_best_bc_college_page = self._install_bc_wallet_page.select_skip() + qrcode = self._connect_with_best_bc_college_page.get_qr_code() return qrcode diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/bc_wallet_showcase_main_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/bc_wallet_showcase_main_page.py new file mode 100644 index 0000000..716e85f --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/bc_wallet_showcase_main_page.py @@ -0,0 +1,27 @@ +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +from agent_factory.bc_showcase.pageobjects.who_do_you_want_to_be_page import WhoDoYouWantToBePage + +# These classes can inherit from a BasePage to do commone setup and functions +class BCWalletShowcaseMainPage(WebBasePage): + """BC Wallet Showcase Main Entry page object""" + + # Locators + on_this_page_text_locator = "BC Wallet Showcase" + #on_this_page_locator = (By.XPATH, '//*[@id="app"]/div/main/div/div/div/div[1]/div/div/h3[1]/strong') + #get_started_locator = (By.XPATH, '//button[@class='bg-bcgov-blue dark:bg-bcgov-white text-bcgov-white dark:text-bcgov-black py-3 px-5 rounded-lg font-semibold shadow-sm dark:shadow-none select-none ']') + get_started_locator = (By.CLASS_NAME, "bg-bcgov-blue") + + + def on_this_page(self): + #return super().on_this_page(self.on_this_page_locator, timeout=20) + return super().on_this_page(self.on_this_page_text_locator, timeout=20) + + def select_get_started(self) -> WhoDoYouWantToBePage: + if self.on_this_page(): + self.find_by(self.get_started_locator).click() + return WhoDoYouWantToBePage(self.driver) + else: + raise Exception(f"App not on the {type(self)} page") + diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/connect_with_best_bc_college_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/connect_with_best_bc_college_page.py new file mode 100644 index 0000000..cc6c417 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/connect_with_best_bc_college_page.py @@ -0,0 +1,45 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 +#from agent_factory.bc_showcase.pageobjects.youre_all_set_page import YoureAllSetPage + + +# These classes can inherit from a BasePage to do commone setup and functions +class ConnectWithBestBCCollegePage(WebBasePage): + """BC Wallet Showcase Connect With Best BC College page object""" + + # Locators + on_this_page_text_locator = "Use your BC Wallet to scan the QR code from the website" + qr_code_locator = (By.XPATH, "//div[@class='relative bg-none']//canvas") + i_already_have_my_credential_locator = (By.XPATH, "//button[normalize-space()='I Already Have my Credential']") + next_locator = (By.XPATH, "//button[normalize-space()='NEXT']") + + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + def get_qr_code(self): + try: + qr_code = self.find_by(self.qr_code_locator, wait_condition=WaitCondition.VISIBILITY_OF_ELEMENT_LOCATED) + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return qr_code + + def select_i_already_have_my_credential(self): + if self.on_this_page(): + self.find_by(self.i_already_have_my_credential_locator).click() + return YoureAllSetPage(self.driver) + else: + raise Exception(f"App not on the {type(self)} page") + + def select_next(self): #-> ConnectWithBestBCCollegePage: + if self.on_this_page(): + self.find_by(self.next_locator).click() + #return ConnectWithBestBCCollegePage(self.driver) + else: + raise Exception(f"App not on the {type(self)} page") diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/install_bc_wallet_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/install_bc_wallet_page.py new file mode 100644 index 0000000..239bcf7 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/install_bc_wallet_page.py @@ -0,0 +1,30 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 +from agent_factory.bc_showcase.pageobjects.connect_with_best_bc_college_page import ConnectWithBestBCCollegePage + + +# These classes can inherit from a BasePage to do commone setup and functions +class InstallBCWalletPage(WebBasePage): + """BC Wallet Showcase Install BC Wallet page object""" + + # Locators + on_this_page_text_locator = "install the BC Wallet app" + skip_locator = (By.XPATH, "//button[normalize-space()='SKIP']") + + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + + def select_skip(self) -> ConnectWithBestBCCollegePage: + try: + self.find_by(self.skip_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return ConnectWithBestBCCollegePage(self.driver) \ No newline at end of file diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/lets_get_started_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/lets_get_started_page.py new file mode 100644 index 0000000..f809c06 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/lets_get_started_page.py @@ -0,0 +1,30 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 +from agent_factory.bc_showcase.pageobjects.install_bc_wallet_page import InstallBCWalletPage + + +# These classes can inherit from a BasePage to do commone setup and functions +class LetsGetStartedPage(WebBasePage): + """BC Wallet Showcase Lets Get Started page object""" + + # Locators + on_this_page_text_locator = "BC Wallet is a new app for storing and using credentials" + next_locator = (By.XPATH, "//button[normalize-space()='NEXT']") + + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + + def select_next(self) -> InstallBCWalletPage: + try: + self.find_by(self.next_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return InstallBCWalletPage(self.driver) \ No newline at end of file diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/who_do_you_want_to_be_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/who_do_you_want_to_be_page.py new file mode 100644 index 0000000..1ecdf32 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/who_do_you_want_to_be_page.py @@ -0,0 +1,50 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 +from agent_factory.bc_showcase.pageobjects.lets_get_started_page import LetsGetStartedPage + + +# These classes can inherit from a BasePage to do commone setup and functions +class WhoDoYouWantToBePage(WebBasePage): + """BC Wallet Showcase Who Do You Want to Be page object""" + + # Locators + on_this_page_text_locator = "Who do you want to be" or "Meet Alice" or "Meet Joyce" + student_locator = (By.XPATH, "//img[@alt='Alice']") + lawyer_locator = (By.XPATH, "//img[@class='m-auto h-16 w-16 p-2 sm:h-20 sm:w-20 md:h-24 md:w-24 md:p-4 lg:h-36 lg:w-36 lg:p-8 rounded-full bg-bcgov-white dark:bg-bcgov-black my-6 shadow shadow-xl ring-4 ring-bcgov-gold']") + next_locator = (By.XPATH, "//button[normalize-space()='NEXT']") + + + def on_this_page(self): + #return super().on_this_page(self.on_this_page_text_locator) + return super().on_this_page(self.student_locator, timeout=20) or super().on_this_page(self.lawyer_locator, timeout=20) + + def select_student(self): + try: + self.find_by(self.student_locator, timeout=30, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + + def select_lawyer(self): + try: + self.find_by(self.lawyer_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + + def select_next(self) -> LetsGetStartedPage: + try: + self.find_by(self.next_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return LetsGetStartedPage(self.driver) diff --git a/aries-mobile-tests/features/bc_wallet/bc_showcase.feature b/aries-mobile-tests/features/bc_wallet/bc_showcase.feature index 470a0e1..1b927e0 100644 --- a/aries-mobile-tests/features/bc_wallet/bc_showcase.feature +++ b/aries-mobile-tests/features/bc_wallet/bc_showcase.feature @@ -6,12 +6,11 @@ Feature: BC Showcase @T001-BCShowcase @critical @AcceptanceTest - Scenario Outline: BC Showcase Student gets access to a store discount - Given a wallet user + Scenario: BC Showcase Student gets access to a store discount + Given an existing Student wallet user | pin | biometrics | | 369369 | off | - - When the has a from + #When the has a from When the Student has credentials | credential | revocable | issuer_agent_type | credential_name | | N/A | False | BCShowcaseBestBCCollegeIssuer | Student Card | @@ -28,10 +27,10 @@ Feature: BC Showcase | proof_result | | Room Booked | - Examples: - | user | issuer_agent_type | credentials | verifier_agent_type | proof_request | proof_result | - | Student | BCShowcaseBestBCCollegeIssuer | Student Card | BCShowcaseBestBCCollegeVerifier | Student Card | Room Booked | - | Student | BCShowcaseBestBCCollegeIssuer | Student Card | BCShowcaseCoolClothesOnlineVerifier | Student Card | Discount | - | Lawyer | BCShowcaseLSBCIssuer | LSBC Member Card:Person | BCShowcaseCourtServicesBranchVerifier | Member Card & Person | Court Services | +# Examples: +# | user | issuer_agent_type | credentials | verifier_agent_type | proof_request | proof_result | +# | Student | BCShowcaseBestBCCollegeIssuer | Student Card | BCShowcaseBestBCCollegeVerifier | Student Card | Room Booked | +# | Student | BCShowcaseBestBCCollegeIssuer | Student Card | BCShowcaseCoolClothesOnlineVerifier | Student Card | Discount | +# | Lawyer | BCShowcaseLSBCIssuer | LSBC Member Card:Person | BCShowcaseCourtServicesBranchVerifier | Member Card & Person | Court Services | -BCShowcaseServiceBCIssuer Person \ No newline at end of file +# BCShowcaseServiceBCIssuer Person \ No newline at end of file diff --git a/aries-mobile-tests/features/steps/bc_wallet/bc_showcase.py b/aries-mobile-tests/features/steps/bc_wallet/bc_showcase.py new file mode 100644 index 0000000..75ed9c2 --- /dev/null +++ b/aries-mobile-tests/features/steps/bc_wallet/bc_showcase.py @@ -0,0 +1,53 @@ +# ----------------------------------------------------------- +# Behave Step Definitions for a proof request to a wallet user +# +# ----------------------------------------------------------- + +from time import sleep +from behave import given, when, then +from decouple import config +from steps.bc_wallet.credential_offer import get_expected_credential_name + +# Local Imports +from agent_controller_client import agent_controller_GET, agent_controller_POST, expected_agent_state, setup_already_connected +from agent_test_utils import get_qr_code_from_invitation, table_to_str, create_non_revoke_interval +# import Page Objects needed +from pageobjects.bc_wallet.holder_get_invite_interface.bc_vp_holder_get_invite_interface import BCVPHolderGetInviteInterface +from pageobjects.bc_wallet.create_bc_digital_id import CreateABCDigitalIDPage + + +@when('the Student has credentials') +def step_impl(context): + + # for row in context.table: + # credential_name = row["credential_name"] + # context.execute_steps(f''' + # Given a connection has been successfully made + # Given the user has a credential offer of {credential} with revocable set as {revokable} + # When they select Accept + # And the holder is informed that their credential is on the way with an indication of loading + # And once the credential arrives they are informed that the Credential is added to your wallet + # And they select Done + # Then they are brought to the list of credentials + # And the credential {credential_name} is accepted is at the top of the list + # ''') + + for row in context.table: + credential_name = row["credential_name"] + qrcode = context.issuer.send_credential(actor=context.actor, schema=credential_name) + context.device_service_handler.inject_qrcode(qrcode) + context.execute_steps(''' + Given they Scan the credential offer QR Code + ''') + + + # context.execute_steps(''' + # When the Holder scans the QR code sent by the "issuer" + # And the Holder is taken to the Connecting Screen/modal + # And the Connecting completes successfully + # Then there is a connection between "issuer" and Holder + # ''') + + + + diff --git a/aries-mobile-tests/features/steps/bc_wallet/wallet_naming.py b/aries-mobile-tests/features/steps/bc_wallet/wallet_naming.py index f734757..825defa 100644 --- a/aries-mobile-tests/features/steps/bc_wallet/wallet_naming.py +++ b/aries-mobile-tests/features/steps/bc_wallet/wallet_naming.py @@ -15,10 +15,16 @@ from pageobjects.bc_wallet.camera_privacy_policy import CameraPrivacyPolicyPage +@step('an existing {actor} wallet user') @step('an existing wallet user') -def an_existing_wallet_user(context): +def an_existing_wallet_user(context, actor=None): """ This is a user who has already setup a wallet (onboarded and setup security), and has closed and reopened the app and authenticated. """ + # If the actor is provided then we will need that for later in whatever scenario is being executed. + # needed for Showcase tests + if actor is not None: + context.actor = actor + # Get PIN and Biometrics setup from context table pin = context.table[0]['pin'] biometrics = context.table[0]['biometrics'] From 0c2de582e5a89198e8351f03178feb28e2161ee1 Mon Sep 17 00:00:00 2001 From: Sheldon Regular Date: Fri, 17 Nov 2023 16:31:57 -0500 Subject: [PATCH 3/3] working student tests and new pipeline run Signed-off-by: Sheldon Regular --- .github/workflows/main.yml | 30 ++- .../agent_factory/agent_interface_factory.py | 4 +- .../bc_showcase_issuer_agent_interface.py | 26 +- .../bc_showcase_verifier_agent_interface.py | 251 ++++++++---------- .../pageobjects/book_a_study_room_page.py | 29 ++ .../connect_with_best_bc_college_page.py | 2 +- .../getting_a_student_discount_page.py | 31 +++ .../start_booking_the_room_page.py | 51 ++++ .../start_proving_youre_a_student_page.py | 52 ++++ .../using_your_credentials_page.py | 42 +++ .../pageobjects/youre_all_set_page.py | 30 +++ .../features/bc_wallet/bc_showcase.feature | 45 +++- .../features/steps/bc_wallet/bc_showcase.py | 46 ++-- .../steps/bc_wallet/credential_offer.py | 3 + .../features/steps/bc_wallet/proof.py | 1 + 15 files changed, 454 insertions(+), 189 deletions(-) create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/book_a_study_room_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/getting_a_student_discount_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/start_booking_the_room_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/start_proving_youre_a_student_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/using_your_credentials_page.py create mode 100644 aries-mobile-tests/agent_factory/bc_showcase/pageobjects/youre_all_set_page.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index de2faee..9b31d2b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -52,6 +52,12 @@ jobs: - mobile-platform: "-p iOS" app-file-name: "-a ${{ needs.check-app-updated.outputs.NEW_APP_NAME }}.ipa" report-project: "bc-digital-id-ios" + - mobile-platform: "-p Android" + app-file-name: "-a ${{ needs.check-app-updated.outputs.NEW_APP_NAME }}.aab" + report-project: "bc-showcase-android" + - mobile-platform: "-p iOS" + app-file-name: "-a ${{ needs.check-app-updated.outputs.NEW_APP_NAME }}.ipa" + report-project: "bc-showcase-ios" #timeout-minutes: 60 steps: - uses: actions/checkout@v2 @@ -133,7 +139,7 @@ jobs: DEVICE_CLOUD_KEY: "-k ${{ secrets.SAUCE_ACCESS_KEY }}" MOBILE_PLATFORM: ${{ matrix.mobile-platform }} APP_FILE_NAME: ${{ matrix.app-file-name }} - TEST_SCOPE: "-t @bc_wallet -t ~@wip -t ~@Connectionless -t ~@BCSC" + TEST_SCOPE: "-t @bc_wallet -t ~@wip -t ~@Connectionless -t ~@BCSC -t ~@BCShowcase" REPORT_PROJECT: ${{ matrix.report-project }} continue-on-error: true @@ -161,7 +167,7 @@ jobs: DEVICE_CLOUD_KEY: "-k ${{ secrets.SAUCE_ACCESS_KEY }}" MOBILE_PLATFORM: ${{ matrix.mobile-platform }} APP_FILE_NAME: ${{ matrix.app-file-name }} - TEST_SCOPE: "-t @bc_wallet -t @Connectionless -t ~@wip -t ~@BCSC" + TEST_SCOPE: "-t @bc_wallet -t @Connectionless -t ~@wip -t ~@BCSC -t ~@BCShowcase" REPORT_PROJECT: ${{ matrix.report-project }} continue-on-error: true @@ -193,6 +199,26 @@ jobs: REPORT_PROJECT: ${{ matrix.report-project }} continue-on-error: true + - name: Run BC Wallet with BC Showcase Tests + if: ${{ contains(matrix.report-project,'bc-showcase') }} + uses: ./.github/workflows/run-test-harness + env: + LEDGER_URL_CONFIG: "http://test.bcovrin.vonx.io" + REGION: "us-west-1" + #TEST_RETRY_ATTEMPTS_OVERRIDE: "2" + with: + MOBILE_WALLET: "-w bc_wallet" + ISSUER_AGENT: '-i "BCShowcaseIssuer;https://bc-wallet-demo-dev.apps.silver.devops.gov.bc.ca/"' + VERIFIER_AGENT: '-v "BCShowcaseVerifier;https://bc-wallet-demo-dev.apps.silver.devops.gov.bc.ca/"' + DEVICE_CLOUD: "-d SauceLabs" + DEVICE_CLOUD_USER: "-u ${{ secrets.SAUCE_USERNAME }}" + DEVICE_CLOUD_KEY: "-k ${{ secrets.SAUCE_ACCESS_KEY }}" + MOBILE_PLATFORM: ${{ matrix.mobile-platform }} + APP_FILE_NAME: ${{ matrix.app-file-name }} + TEST_SCOPE: "-t @bc_wallet -t @BCShowcase -t ~@wip" + REPORT_PROJECT: ${{ matrix.report-project }} + continue-on-error: true + - name: Upload AMTH All Test results to Allure uses: ./.github/workflows/run-send-gen-test-results-secure with: diff --git a/aries-mobile-tests/agent_factory/agent_interface_factory.py b/aries-mobile-tests/agent_factory/agent_interface_factory.py index fba0b11..2d8a09a 100644 --- a/aries-mobile-tests/agent_factory/agent_interface_factory.py +++ b/aries-mobile-tests/agent_factory/agent_interface_factory.py @@ -3,6 +3,7 @@ given the agent type passed in. """ from agent_factory.bc_showcase.bc_showcase_issuer_agent_interface import BCShowcaseIssuerAgentInterface +from agent_factory.bc_showcase.bc_showcase_verifier_agent_interface import BCShowcaseVerifierAgentInterface from agent_factory.issuer_agent_interface import IssuerAgentInterface from agent_factory.verifier_agent_interface import VerifierAgentInterface from agent_factory.aath.aath_issuer_agent_interface import AATHIssuerAgentInterface @@ -22,7 +23,8 @@ class AgentInterfaceFactory(): } verifier_agent_type_interface_dict = { "AATH": AATHVerifierAgentInterface, - "BC_Person_Showcase": BCPersonShowcaseVerifierAgentInterface + "BC_Person_Showcase": BCPersonShowcaseVerifierAgentInterface, + "BCShowcaseVerifier": BCShowcaseVerifierAgentInterface } def create_issuer_agent_interface(self, agent_type, agent_endpoint) -> IssuerAgentInterface: diff --git a/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py index a5d63d2..d5cb53a 100644 --- a/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py +++ b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_issuer_agent_interface.py @@ -2,6 +2,7 @@ Class for BC Showcase issuer agent for Student and Lawer Showcase Credentials """ import base64 +import io from agent_factory.issuer_agent_interface import IssuerAgentInterface from sys import platform from selenium import webdriver @@ -76,32 +77,27 @@ def connected(self): def send_credential(self, actor:str, credential_offer=None, version=1, schema=None, revokable=False): """send a credential to the holder, returns a qr code for holder to connect to""" self._who_do_you_want_to_be_page = self._bc_wallet_showcase_main_page.select_get_started() + self.driver.minimize_window() + self.driver.maximize_window() if actor == "Student": self._who_do_you_want_to_be_page.select_student() elif actor == "Lawyer": self._who_do_you_want_to_be_page.select_lawyer() else: raise Exception(f"Unknown actor type {actor}") + self.driver.minimize_window() + self.driver.maximize_window() self._lets_get_started_page = self._who_do_you_want_to_be_page.select_next() self._install_bc_wallet_page = self._lets_get_started_page.select_next() self._connect_with_best_bc_college_page = self._install_bc_wallet_page.select_skip() + self.driver.minimize_window() + self.driver.maximize_window() qrcode = self._connect_with_best_bc_college_page.get_qr_code() - return qrcode + + contents = qrcode.screenshot_as_base64.encode('utf-8') + return contents.decode('utf-8') - def restart_issue_credential(self): - # go to the issuer endpoint in the browser - self.driver.get(self.endpoint) - # make sure we are on the first page, the terms of service page - if not self._terms_of_service_page.on_this_page(): - raise Exception('Something is wrong, not on the Terms of Service Page for the CANdy UVP Issuer') - def revoke_credential(self, publish_immediately=True, notify_holder=False): """revoke a credential""" - return Exception('Function not supported for CANdy UVP Issuer') - - def _create_name_value_pairs_from_credential_offer(self, credential_offer): - credential_data = {} - for attribute in credential_offer["attributes"]: - credential_data[attribute["name"]] = attribute["value"] - return credential_data \ No newline at end of file + return Exception('Function not supported for BC Showcase Issuer') \ No newline at end of file diff --git a/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_verifier_agent_interface.py b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_verifier_agent_interface.py index 51d9564..0f8a63f 100644 --- a/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_verifier_agent_interface.py +++ b/aries-mobile-tests/agent_factory/bc_showcase/bc_showcase_verifier_agent_interface.py @@ -1,160 +1,137 @@ """ -Absctact Base Class for actual verifier agent interfaces to implement +Class for BC Showcase verifier agent for Student and Lawer Showcase Proofs """ from asyncio import sleep from agent_factory.verifier_agent_interface import VerifierAgentInterface +from sys import platform +from selenium import webdriver +from selenium.webdriver.chrome.service import Service +from selenium.webdriver.chrome.options import Options +from webdriver_manager.chrome import ChromeDriverManager +from webdriver_manager.core.os_manager import ChromeType import json from agent_test_utils import get_qr_code_from_invitation from agent_controller_client import agent_controller_GET, agent_controller_POST, expected_agent_state, setup_already_connected - +# import Page Objects needed +from agent_factory.bc_showcase.pageobjects.bc_wallet_showcase_main_page import BCWalletShowcaseMainPage +from agent_factory.bc_showcase.pageobjects.who_do_you_want_to_be_page import WhoDoYouWantToBePage +from agent_factory.bc_showcase.pageobjects.lets_get_started_page import LetsGetStartedPage +from agent_factory.bc_showcase.pageobjects.install_bc_wallet_page import InstallBCWalletPage +from agent_factory.bc_showcase.pageobjects.connect_with_best_bc_college_page import ConnectWithBestBCCollegePage +from agent_factory.bc_showcase.pageobjects.youre_all_set_page import YoureAllSetPage +from agent_factory.bc_showcase.pageobjects.using_your_credentials_page import UsingYourCredentialsPage +from agent_factory.bc_showcase.pageobjects.getting_a_student_discount_page import GettingAStudentDiscountPage +from agent_factory.bc_showcase.pageobjects.start_proving_youre_a_student_page import StartProvingYoureAStudentPage +from agent_factory.bc_showcase.pageobjects.book_a_study_room_page import BookAStudyRoomPage +from agent_factory.bc_showcase.pageobjects.start_booking_the_room_page import StartBookingTheRoomPage class BCShowcaseVerifierAgentInterface(VerifierAgentInterface): - # Default schema and cred - DEFAULT_PROOF_REQUEST = { - "requested_attributes": { - "Person":{ - "restrictions":[ - { - "schema_name":"Person" - } - ], - "names":[ - "picture", - "family_name", - "given_names", - "locality", - "region", - "postal_code", - "street_address", - "country", - "expiry_date_dateint", - "birthdate_dateint" - ] - } - }, - "requested_predicates":{ - - }, - "version":"1.0.0", - "name":"BC Digital ID Request" - } + _actor : str + _bc_wallet_showcase_main_page: BCWalletShowcaseMainPage + _who_do_you_want_to_be_page: WhoDoYouWantToBePage + _lets_get_started_page: LetsGetStartedPage + _install_bc_wallet_page: InstallBCWalletPage + _connect_with_best_bc_college_page: ConnectWithBestBCCollegePage + _youre_all_set_page: YoureAllSetPage + _using_your_credentials_page: UsingYourCredentialsPage + + # Cool Clothes Online Page Objects + _getting_a_student_discount_page: GettingAStudentDiscountPage + _start_proving_youre_a_student_page: StartProvingYoureAStudentPage + + # BestBC College Page Objects + _book_a_study_room_page: BookAStudyRoomPage + _start_booking_the_room_page: StartBookingTheRoomPage + + + def __init__(self, endpoint): + # Standup Selenuim Driver with endpoint + super().__init__(endpoint) + if platform == "linux" or platform == "linux2": + print("Starting Chromium on linux for BC Showcase Issuer Agent") + options = Options() + options.add_argument("--no-sandbox") + options.add_argument("--disable-dev-shm-usage") + options.add_argument("--headless") + self.driver = webdriver.Chrome(options=options, service=Service(ChromeDriverManager(chrome_type=ChromeType.CHROMIUM).install())) + else: + print("Starting Chrome on Mac or Windows for Issuer Agent") + self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) + # go to the issuer endpoint in the browser + self.driver.get(self.endpoint) + # instantiate intial page objects and navigate to the Proof portion of the BC Wallet Showcase + self._bc_wallet_showcase_main_page = BCWalletShowcaseMainPage(self.driver) + + # make sure we are on the page for proofs in the Showcase. + if not self._bc_wallet_showcase_main_page.on_this_page(): + raise Exception('Something is wrong, not on the BC Wallet Showcase Main Page') + def get_issuer_type(self) -> str: - """return the type of issuer as a string BCPersonShowcaseVerifier""" - return "BCPersonShowcaseVerifier" + """return the type of issuer as a string BCShowcaseVerifier""" + return "BCShowcaseVerifier" def create_invitation(self, oob=False, print_qrcode=False, save_qrcode=False, qr_code_border=40): - """create an invitation and return the json back to the caller """ - # https://bc-wallet-demo-agent-admin-test.apps.silver.devops.gov.bc.ca/connections/create-invitation - - self._oob = oob - if self._oob is True: - data = {"use_public_did": False} - (resp_status, resp_text) = agent_controller_POST( - self.endpoint + "/agent/command/", - "out-of-band", - operation="send-invitation-message", - data=data, - ) - else: - (resp_status, resp_text) = agent_controller_POST( - self.endpoint, topic="/connections", operation="create-invitation" - ) - - if resp_status != 200: - raise Exception( - f"Call to create connection invitation failed: {resp_status}; {resp_text}" - ) - else: - self.invitation_json = json.loads(resp_text) - qrimage = get_qr_code_from_invitation(self.invitation_json, print_qrcode, save_qrcode, qr_code_border) - return qrimage + # This is not supported on the BC Showcase Verifier. Connection is made when sending the proof request + # If called, send an exception back on this one and let the test handle it. Maybe a VerifierInterfaceFunctionNotSupported error. + return Exception('Function not supported for BC Showcase Verifier') def connected(self): """return True/False indicating if this issuer is connected to the wallet holder """ + """return true if connected""" + return self._connect_with_best_bc_college_page.connected() - # If OOB then make a call to get the connection id from the webhook. - if self._oob == True: - # Get the responders's connection id from the above request's response webhook in the backchannel - invitation_id = self.invitation_json["invitation"]["@id"] - (resp_status, resp_text) = agent_controller_GET( - self.endpoint + "/agent/response/", "did-exchange", id=invitation_id - ) - if resp_status != 200: - raise Exception( - f"Call get the connection id from the OOB connection failed: {resp_status}; {resp_text}" - ) - else: - resp_json = json.loads(resp_text) - connection_id = resp_json["connection_id"] - self.invitation_json["connection_id"] = connection_id - else: - connection_id = self.invitation_json['connection']['id'] - - return self._expected_connection_state(self.endpoint, "/connections", connection_id, "complete", 6, 2) - - - def _expected_connection_state(self, agent_url, protocol_txt, id, status_txt, wait_time=2.0, sleep_time=0.5): - sleep(sleep_time) - state = "None" - if type(status_txt) != list: - status_txt = [status_txt] - # "N/A" means that the controller can't determine the state - we'll treat this as a successful response - status_txt.append("N/A") - #for i in range(int(wait_time/sleep_time)): - for i in range(int(wait_time)): - (resp_status, resp_text) = agent_controller_GET(agent_url, protocol_txt, id=id) - if resp_status == 200: - resp_json = json.loads(resp_text) - state = resp_json["state"] - if state in status_txt: - return True - sleep(sleep_time) - - print("From", agent_url, "Expected state", status_txt, "but received", state, ", with a response status of", resp_status) - return False - - def send_proof_request(self, version=1, request_for_proof=None, connectionless=False): + + def send_proof_request(self, actor:str, proof:str, version=1, request_for_proof=None, connectionless=False): """create a proof request """ - - if version == 2: - topic = "proof-v2" + self._who_do_you_want_to_be_page = self._bc_wallet_showcase_main_page.select_get_started() + self.driver.minimize_window() + self.driver.maximize_window() + if actor == "Student": + self._who_do_you_want_to_be_page.select_student() + elif actor == "Lawyer": + self._who_do_you_want_to_be_page.select_lawyer() else: - topic = "/proofs" - - if request_for_proof: + raise Exception(f"Unknown actor type {actor}") + self.driver.minimize_window() + self.driver.maximize_window() + self._lets_get_started_page = self._who_do_you_want_to_be_page.select_next() + self._install_bc_wallet_page = self._lets_get_started_page.select_next() + self._connect_with_best_bc_college_page = self._install_bc_wallet_page.select_skip() + self.driver.minimize_window() + self.driver.maximize_window() + self._youre_all_set_page = self._connect_with_best_bc_college_page.select_i_already_have_my_credential() + self._using_your_credentials_page = self._youre_all_set_page.select_finish() + + if proof == "Cool Clothes Online": + self._getting_a_student_discount_page = self._using_your_credentials_page.select_cool_clothes_online_start() + self.driver.minimize_window() + self.driver.maximize_window() + self._start_proving_youre_a_student_page = self._getting_a_student_discount_page.select_start() + self.driver.minimize_window() + self.driver.maximize_window() + qrcode = self._start_proving_youre_a_student_page.get_qr_code() + elif proof == "BestBC College": + self._book_a_study_room_page = self._using_your_credentials_page.select_bestbc_college_start() + self.driver.minimize_window() + self.driver.maximize_window() + self._start_booking_the_room_page = self._book_a_study_room_page.select_start() + self.driver.minimize_window() + self.driver.maximize_window() + qrcode = self._start_booking_the_room_page.get_qr_code() + + contents = qrcode.screenshot_as_base64.encode('utf-8') + return contents.decode('utf-8') + + + def proof_success(self, proof_result): + if proof_result == "Discount": + return self._start_proving_youre_a_student_page.proof_success() + elif proof_result == "Room Booked": + return self._start_booking_the_room_page.proof_success() + elif proof_result == "Court Services": pass - # if context.non_revoked_timeframe: - # data["non_revoked"] = context.non_revoked_timeframe["non_revoked"] - else: - request_for_proof = self.DEFAULT_PROOF_REQUEST.copy() - - presentation_request = { - "connectionId": self.invitation_json['connection']['id'], - "proofRequest": request_for_proof, - "comment": f"proof request from {self.get_issuer_type()} {self.endpoint}" - } - - if connectionless: - operation = "create-send-connectionless-request" else: - #presentation_request["connectionId"] = self.invitation_json['connection']['id'] - operation = "request-proof" - - (resp_status, resp_text) = agent_controller_POST( - self.endpoint, - topic, - operation=operation, - data=presentation_request, - wrap_data_with_data=False - ) - if resp_status != 200: - raise Exception( - f"Call to send proof request failed: {resp_status}; {resp_text}" - ) - else: - self.proof_request_json = json.loads(resp_text) - - + raise Exception(f"Invalid proof result: {proof_result} expected Discount, Room Booked, or Court Services") diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/book_a_study_room_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/book_a_study_room_page.py new file mode 100644 index 0000000..714fee5 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/book_a_study_room_page.py @@ -0,0 +1,29 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 +from agent_factory.bc_showcase.pageobjects.start_booking_the_room_page import StartBookingTheRoomPage + + +# These classes can inherit from a BasePage to do commone setup and functions +class BookAStudyRoomPage(WebBasePage): + """BC Wallet Showcase Book a Study Room page object""" + + # Locators + on_this_page_text_locator = "needs a study room for some peace and quiet" + start_locator = (By.XPATH, "//button[normalize-space()='START']") + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + + def select_start(self) -> StartBookingTheRoomPage: + try: + self.find_by(self.start_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return StartBookingTheRoomPage(self.driver) diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/connect_with_best_bc_college_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/connect_with_best_bc_college_page.py index cc6c417..e33841c 100644 --- a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/connect_with_best_bc_college_page.py +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/connect_with_best_bc_college_page.py @@ -3,7 +3,7 @@ from selenium.webdriver.common.by import By from pageobjects.basepage import WaitCondition import base64 -#from agent_factory.bc_showcase.pageobjects.youre_all_set_page import YoureAllSetPage +from agent_factory.bc_showcase.pageobjects.youre_all_set_page import YoureAllSetPage # These classes can inherit from a BasePage to do commone setup and functions diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/getting_a_student_discount_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/getting_a_student_discount_page.py new file mode 100644 index 0000000..83f3c97 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/getting_a_student_discount_page.py @@ -0,0 +1,31 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 +from agent_factory.bc_showcase.pageobjects.start_proving_youre_a_student_page import StartProvingYoureAStudentPage + + +# These classes can inherit from a BasePage to do commone setup and functions +class GettingAStudentDiscountPage(WebBasePage): + """BC Wallet Showcase Getting a Student Discount page object""" + + # Locators + on_this_page_text_locator = "get a student discount on her online purchase" + start_locator = (By.XPATH, "(//button[normalize-space()='START'])[1]") + + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + + def select_start(self) -> StartProvingYoureAStudentPage: + try: + self.find_by(self.start_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return StartProvingYoureAStudentPage(self.driver) + \ No newline at end of file diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/start_booking_the_room_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/start_booking_the_room_page.py new file mode 100644 index 0000000..96b8ebc --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/start_booking_the_room_page.py @@ -0,0 +1,51 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 + + +# These classes can inherit from a BasePage to do commone setup and functions +class StartBookingTheRoomPage(WebBasePage): + """BC Wallet Showcase Start Booking the Room page object""" + + # Locators + on_this_page_text_locator = "Imagine you're on the room booking page for BestBC College" + qr_code_locator = (By.XPATH, "//div[@class='relative bg-none']//canvas") + next_locator = (By.XPATH, "//button[normalize-space()='NEXT']") + proof_success_locator = (By.XPATH, "(//p[normalize-space()='Success! You can continue.'])[1]") + complete_locator = (By.XPATH, "(//button[contains(@class,'cursor-pointer dark:text-white')])[1]") + + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + def get_qr_code(self): + try: + qr_code = self.find_by(self.qr_code_locator, wait_condition=WaitCondition.VISIBILITY_OF_ELEMENT_LOCATED) + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return qr_code + + + def select_next(self): #-> ConnectWithBestBCCollegePage: + try: + self.find_by(self.next_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + #return ConnectWithBestBCCollegePage(self.driver) + + def proof_success(self) -> bool: + try: + self.find_by(self.proof_success_locator, wait_condition=WaitCondition.VISIBILITY_OF_ELEMENT_LOCATED) + self.select_next() + self.find_by(self.complete_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + return True + except Exception as e: + return False \ No newline at end of file diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/start_proving_youre_a_student_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/start_proving_youre_a_student_page.py new file mode 100644 index 0000000..af8096c --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/start_proving_youre_a_student_page.py @@ -0,0 +1,52 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 + + +# These classes can inherit from a BasePage to do commone setup and functions +class StartProvingYoureAStudentPage(WebBasePage): + """BC Wallet Showcase Start Proving You're a Student page object""" + + # Locators + on_this_page_text_locator = "you are in the checkout process for Cool Clothes Online" + qr_code_locator = (By.XPATH, "//div[@class='relative bg-none']//canvas") + next_locator = (By.XPATH, "(//button[normalize-space()='NEXT'])[1]") + proof_success_text_locator = "Success! You can continue." + proof_success_locator = (By.XPATH, "(//p[normalize-space()='Success! You can continue.'])[1]") + complete_locator = (By.XPATH, "(//button[contains(@class,'cursor-pointer dark:text-white')])[1]") + + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + def get_qr_code(self): + try: + qr_code = self.find_by(self.qr_code_locator, wait_condition=WaitCondition.VISIBILITY_OF_ELEMENT_LOCATED) + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return qr_code + + + def select_next(self): #-> ConnectWithBestBCCollegePage: + try: + self.find_by(self.next_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + #return ConnectWithBestBCCollegePage(self.driver) + + def proof_success(self) -> bool: + try: + self.find_by(self.proof_success_locator, wait_condition=WaitCondition.VISIBILITY_OF_ELEMENT_LOCATED) + self.select_next() + self.find_by(self.complete_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + return True + except Exception as e: + return False diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/using_your_credentials_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/using_your_credentials_page.py new file mode 100644 index 0000000..507d0ce --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/using_your_credentials_page.py @@ -0,0 +1,42 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 +from agent_factory.bc_showcase.pageobjects.getting_a_student_discount_page import GettingAStudentDiscountPage +from agent_factory.bc_showcase.pageobjects.book_a_study_room_page import BookAStudyRoomPage + + +# These classes can inherit from a BasePage to do commone setup and functions +class UsingYourCredentialsPage(WebBasePage): + """BC Wallet Showcase Using Your Credentials page object""" + + # Locators + on_this_page_text_locator = "You'll be asked to share" + cool_clothes_online_start_locator = (By.XPATH, "(//button[@class='text-sm bg-bcgov-blue dark:bg-bcgov-white text-white dark:text-black w-24 h-8 py-1.5 px-4 rounded font-semibold shadow-sm opacity-100'][normalize-space()='START'])[1]") + bestbc_online_start_locator = (By.XPATH, "(//button[@class='text-sm bg-bcgov-blue dark:bg-bcgov-white text-white dark:text-black w-24 h-8 py-1.5 px-4 rounded font-semibold shadow-sm opacity-100'][normalize-space()='START'])[2]") + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + + def select_cool_clothes_online_start(self) -> GettingAStudentDiscountPage: + try: + self.find_by(self.cool_clothes_online_start_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return GettingAStudentDiscountPage(self.driver) + + + def select_bestbc_college_start(self) -> BookAStudyRoomPage: + try: + self.find_by(self.bestbc_online_start_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return BookAStudyRoomPage(self.driver) \ No newline at end of file diff --git a/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/youre_all_set_page.py b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/youre_all_set_page.py new file mode 100644 index 0000000..382fba6 --- /dev/null +++ b/aries-mobile-tests/agent_factory/bc_showcase/pageobjects/youre_all_set_page.py @@ -0,0 +1,30 @@ +from PIL import Image +from agent_factory.candy_uvp.pageobjects.webbasepage import WebBasePage +from selenium.webdriver.common.by import By +from pageobjects.basepage import WaitCondition +import base64 +from agent_factory.bc_showcase.pageobjects.using_your_credentials_page import UsingYourCredentialsPage + + +# These classes can inherit from a BasePage to do commone setup and functions +class YoureAllSetPage(WebBasePage): + """BC Wallet Showcase Your All Set page object""" + + # Locators + on_this_page_text_locator = "you’ve just received your first digital credentials" + finish_locator = (By.XPATH, "//button[normalize-space()='FINISH']") + + + def on_this_page(self): + return super().on_this_page(self.on_this_page_text_locator) + + + def select_finish(self) -> UsingYourCredentialsPage: + try: + self.find_by(self.finish_locator, wait_condition=WaitCondition.ELEMENT_TO_BE_CLICKABLE).click() + except Exception as e: + if not self.on_this_page(): + raise Exception(f"App not on the {type(self)} page") + else: + raise e + return UsingYourCredentialsPage(self.driver) \ No newline at end of file diff --git a/aries-mobile-tests/features/bc_wallet/bc_showcase.feature b/aries-mobile-tests/features/bc_wallet/bc_showcase.feature index 1b927e0..6a338cb 100644 --- a/aries-mobile-tests/features/bc_wallet/bc_showcase.feature +++ b/aries-mobile-tests/features/bc_wallet/bc_showcase.feature @@ -10,27 +10,46 @@ Feature: BC Showcase Given an existing Student wallet user | pin | biometrics | | 369369 | off | - #When the has a from When the Student has credentials | credential | revocable | issuer_agent_type | credential_name | | N/A | False | BCShowcaseBestBCCollegeIssuer | Student Card | - - And the has a proof request of from And the Student has a proof request - | verifier_agent_type | proof_request | - | BCShowcaseBestBCCollegeVerifier | Student Card | - + | verifier_agent_type | proof_request | + | BCShowcaseBestBCCollegeVerifier | Cool Clothes Online | And they select Share + Then they have Access + | proof_result | + | Discount | - Then they have + @T002-BCShowcase @critical @AcceptanceTest + Scenario: BC Showcase Student gets access to a book a room + Given an existing Student wallet user + | pin | biometrics | + | 369369 | off | + When the Student has credentials + | credential | revocable | issuer_agent_type | credential_name | + | N/A | False | BCShowcaseBestBCCollegeIssuer | Student Card | + And the Student has a proof request + | verifier_agent_type | proof_request | + | BCShowcaseBestBCCollegeVerifier | BestBC College | + And they select Share Then they have Access | proof_result | | Room Booked | -# Examples: -# | user | issuer_agent_type | credentials | verifier_agent_type | proof_request | proof_result | -# | Student | BCShowcaseBestBCCollegeIssuer | Student Card | BCShowcaseBestBCCollegeVerifier | Student Card | Room Booked | -# | Student | BCShowcaseBestBCCollegeIssuer | Student Card | BCShowcaseCoolClothesOnlineVerifier | Student Card | Discount | -# | Lawyer | BCShowcaseLSBCIssuer | LSBC Member Card:Person | BCShowcaseCourtServicesBranchVerifier | Member Card & Person | Court Services | -# BCShowcaseServiceBCIssuer Person \ No newline at end of file + @T003-BCShowcase @critical @AcceptanceTest @wip + Scenario: BC Showcase Lawyer gets access to court services + Given an existing Lawyer wallet user + | pin | biometrics | + | 369369 | off | + When the Lawyer has credentials + | credential | revocable | issuer_agent_type | credential_name | + | N/A | False | BCShowcaseLSBCIssuer | LSBC Member Card:Person | + And the Lawyer has a proof request + | verifier_agent_type | proof_request | + | BCShowcaseCourtServicesBranchVerifier | Member Card & Person | + And they select Share + Then they have Access + | proof_result | + | Court Services | \ No newline at end of file diff --git a/aries-mobile-tests/features/steps/bc_wallet/bc_showcase.py b/aries-mobile-tests/features/steps/bc_wallet/bc_showcase.py index 75ed9c2..89ac46a 100644 --- a/aries-mobile-tests/features/steps/bc_wallet/bc_showcase.py +++ b/aries-mobile-tests/features/steps/bc_wallet/bc_showcase.py @@ -19,35 +19,41 @@ @when('the Student has credentials') def step_impl(context): - # for row in context.table: - # credential_name = row["credential_name"] - # context.execute_steps(f''' - # Given a connection has been successfully made - # Given the user has a credential offer of {credential} with revocable set as {revokable} - # When they select Accept - # And the holder is informed that their credential is on the way with an indication of loading - # And once the credential arrives they are informed that the Credential is added to your wallet - # And they select Done - # Then they are brought to the list of credentials - # And the credential {credential_name} is accepted is at the top of the list - # ''') - for row in context.table: credential_name = row["credential_name"] qrcode = context.issuer.send_credential(actor=context.actor, schema=credential_name) context.device_service_handler.inject_qrcode(qrcode) context.execute_steps(''' Given they Scan the credential offer QR Code + When the holder opens the credential offer + Then holder is brought to the credential offer screen + When they select Accept + And the holder is informed that their credential is on the way with an indication of loading + And once the credential arrives they are informed that the Credential is added to your wallet + And they select Done + Then they are brought to the list of credentials ''') - # context.execute_steps(''' - # When the Holder scans the QR code sent by the "issuer" - # And the Holder is taken to the Connecting Screen/modal - # And the Connecting completes successfully - # Then there is a connection between "issuer" and Holder - # ''') - +@when('the Student has a proof request') +def step_impl(context): + for row in context.table: + proof_request_name = row["proof_request"] + qrcode = context.verifier.send_proof_request(actor=context.actor, proof=proof_request_name) + context.device_service_handler.inject_qrcode(qrcode) + context.execute_steps(''' + Given they Scan the proof request QR Code + When the holder opens the proof request + Then holder is brought to the proof request + ''') + # And they select Share + # And the holder is informed that they are sending information securely + # Then they are informed that the information sent successfully +@then('they have Access') +def step_impl(context): + for row in context.table: + proof_result = row["proof_result"] + assert context.verifier.proof_success(proof_result) \ No newline at end of file diff --git a/aries-mobile-tests/features/steps/bc_wallet/credential_offer.py b/aries-mobile-tests/features/steps/bc_wallet/credential_offer.py index ff6265e..de9aacf 100644 --- a/aries-mobile-tests/features/steps/bc_wallet/credential_offer.py +++ b/aries-mobile-tests/features/steps/bc_wallet/credential_offer.py @@ -14,6 +14,7 @@ #from pageobjects.bc_wallet.credential_offer_notification import CredentialOfferNotificationPage from pageobjects.bc_wallet.credential_offer import CredentialOfferPage from pageobjects.bc_wallet.credential_added import CredentialAddedPage +from pageobjects.bc_wallet.contact import ContactPage @given('a connection has been successfully made') @@ -41,6 +42,8 @@ def step_impl(context): @step('the holder opens the credential offer') def step_impl(context): + if 'thisContactPage' not in context: + context.thisContactPage = ContactPage(context.driver) # Select the credential offer context.thisCredentialOfferPage = context.thisContactPage.select_open_credential_offer() diff --git a/aries-mobile-tests/features/steps/bc_wallet/proof.py b/aries-mobile-tests/features/steps/bc_wallet/proof.py index 0996509..6b6327d 100644 --- a/aries-mobile-tests/features/steps/bc_wallet/proof.py +++ b/aries-mobile-tests/features/steps/bc_wallet/proof.py @@ -413,6 +413,7 @@ def step_impl(context, credential): '''.format(table=table_to_str(context.table))) +@given('they Scan the proof request QR Code') @given('they Scan the credential offer QR Code') def step_impl(context): if hasattr(context, 'thisNavBar') == False: