> replyProducer = ReplyMethods.ALWAYS_VALID;
- private Channel channel;
- private Json laoCreationMessageData;
-
- private String laoID;
-
- public MockBackend(MessageQueue queue, int port) {
- this.queue = queue;
- server = new WebSocketServerBase(port, "/", this);
- logger.info("Mock Backend started");
- }
-
- /**
- * Sets the reply producer of the backend.
- *
- * It can be set to null if no reply should be sent back
- *
- * @param replyProducer to set
- */
- public void setReplyProducer(Function> replyProducer) {
- this.replyProducer = replyProducer;
- }
-
- @Override
- public void channelActive(ChannelHandlerContext ctx) {
- channel = ctx.channel();
- connected.complete(true);
- logger.trace("Client connected from the server side");
- }
-
- // As this object is the channel handler of the server, this function is called whenever a new
- // message is received by it.
- // The text message is held is a TextWebSocketFrame which is the primitive that is sent over the
- // network
- @Override
- protected void channelRead0(
- ChannelHandlerContext channelHandlerContext, TextWebSocketFrame frame) {
- String frameText = frame.text();
- logger.info("message received : {}", frameText);
- if (!frameText.toLowerCase().contains(CONSENSUS) && !frameText.toLowerCase().contains(COIN) && !frameText.contains(SOCIAL)) {
- // We don't want consensus or coin messages to interfere since we do not test them yet
- queue.onNewMsg(frameText);
- }
- if (replyProducer != null) replyProducer.apply(frameText).forEach(this::send);
- }
-
- public int getPort() {
- return server.getPort();
- }
-
- public boolean waitForConnection(long timeout) {
- logger.info("Waiting for connection...");
- long start = System.currentTimeMillis();
- try {
- connected.get(timeout, TimeUnit.MILLISECONDS);
- logger.info("Connection established in {}s", (System.currentTimeMillis() - start) / 1000.0);
- return true;
- } catch (InterruptedException | ExecutionException e) {
- e.printStackTrace();
- return false;
- } catch (TimeoutException e) {
- logger.error("timeout while waiting for connection to backend");
- return false;
- }
- }
-
- public boolean isConnected() {
- return connected.isDone();
- }
-
- public void stop() {
- logger.info("stopping server...");
- server.stop();
- }
-
- public void send(String text) {
- logger.info("sending message : {}", text);
- channel.eventLoop().submit(() -> channel.writeAndFlush(new TextWebSocketFrame(text)));
- }
-
- public MessageBuffer getBuffer() {
- return queue;
- }
-
- /** Empties the buffer */
- public void clearBuffer() {
- logger.info("Buffer cleared");
- queue.clear();
- }
-
- /**
- * @return true if the message buffer is empty
- */
- public boolean receiveNoMoreResponses() {
- return queue.takeTimeout(5000) == null;
- }
-
- /**
- * Backend behaviour is specific to Lao Creation. It stores publish message and replies with a
- * valid message It also replies with valid to subscribe and with the Lao creation message to the
- * catch-up
- */
- public void setLaoCreateMode() {
- replyProducer = ReplyMethods.CATCHUP_VALID_RESPONSE;
- }
-
- /**
- * Backend behaviour is to respond to publish message with both broadcast and a valid response. It
- * replies with valid to subscribes and empty (valid) message to catch-ups
- */
- public void setValidBroadcastMode() {
- replyProducer = ReplyMethods.BROADCAST_VALID_RESPONSE;
- }
-}
diff --git a/tests/karate/src/test/java/fe/net/ReplyMethods.java b/tests/karate/src/test/java/fe/net/ReplyMethods.java
deleted file mode 100644
index a1d856dce1..0000000000
--- a/tests/karate/src/test/java/fe/net/ReplyMethods.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package fe.net;
-
-import com.intuit.karate.Json;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-
-import static common.utils.Constants.*;
-import static common.utils.JsonUtils.getJSON;
-
-/**
- * This class contains useful message replies that can be used to tailor the response of the
- * mockbackend depending on what action is mocked {@link MockBackend#setReplyProducer(Function)}
- */
-public class ReplyMethods {
-
- private static final String VALID_REPLY_TEMPLATE =
- "{\"jsonrpc\":\"2.0\",\"id\":%ID%,\"result\":0}";
- private static final String VALID_CATCHUP_REPLY_TEMPLATE =
- "{\"jsonrpc\":\"2.0\",\"id\":%ID%,\"result\":[]}";
-
- private static final String RC_CREATE_BROADCAST_TEMPLATE =
- "{\"jsonrpc\":\"2.0\",\"method\": \"broadcast\",\"params\":%PARAM%}";
-
- private static Json laoCreatePublishJson;
-
- private static List buildSingleton(String string) {
- return Collections.singletonList(string);
- }
-
- /** Always reply with a valid response */
- public static Function> ALWAYS_VALID =
- msg -> {
- Json msgJson = Json.of(msg);
- int id = msgJson.get(ID);
- String template =
- msgJson.get(METHOD).equals(CATCHUP)
- ? VALID_CATCHUP_REPLY_TEMPLATE
- : VALID_REPLY_TEMPLATE;
- return buildSingleton(template.replace("%ID%", Integer.toString(id)));
- };
-
- public static Function> LAO_CREATE_CATCHUP =
- msg -> {
- if (msg.contains(CONSENSUS) || msg.contains(COIN)) {
- return ALWAYS_VALID.apply(msg);
- }
- Json msgJson = Json.of(msg);
- String replaceId =
- VALID_CATCHUP_REPLY_TEMPLATE.replace("%ID%", Integer.toString(msgJson.get(ID)));
- if (laoCreatePublishJson == null) {
- throw new IllegalStateException(
- "When creating a catchup the laoCreate should not be null");
- }
- return buildSingleton(replaceId.replace("[]", "[" + laoCreatePublishJson + "]"));
- };
-
- /**
- * This returns a valid reply to subscribe messages and replies with the published lao to catch-ups
- * It is specific to the LAO creation process
- */
- public static Function> CATCHUP_VALID_RESPONSE =
- msg -> {
- Json msgJson = Json.of(msg);
- String method = msgJson.get(METHOD);
- if (PUBLISH.equals(method)) {
- laoCreatePublishJson = getJSON(getJSON(Json.of(msg), PARAMS), MESSAGE);
- }
- if (CATCHUP.equals(method)) {
- return LAO_CREATE_CATCHUP.apply(msg);
- } else { // We want to respond valid result for both publish and subscribe
- return ALWAYS_VALID.apply(msg);
- }
- };
-
- /** This replies with a broadcast of the publish message and a valid response */
- public static Function> BROADCAST_VALID_RESPONSE =
- msg -> {
- Json msgJson = Json.of(msg);
- String method = msgJson.get(METHOD);
-
- if (PUBLISH.equals(method)) {
- Json param = getJSON(Json.of(msg), PARAMS);
- String channel = param.get(CHANNEL);
-
- Map msgMap = param.get(MESSAGE);
- Json send = Json.object();
- send.set(CHANNEL, channel);
- send.set(MESSAGE, msgMap);
-
- String broadCast = RC_CREATE_BROADCAST_TEMPLATE.replace("%PARAM%", send.toString());
- String result = ALWAYS_VALID.apply(msg).get(0);
-
- return Arrays.asList(broadCast, result);
- } else {
- return ALWAYS_VALID.apply(msg);
- }
- };
-}
diff --git a/tests/karate/src/test/java/fe/net/mockBackend.feature b/tests/karate/src/test/java/fe/net/mockBackend.feature
deleted file mode 100644
index ad2f1c0d00..0000000000
--- a/tests/karate/src/test/java/fe/net/mockBackend.feature
+++ /dev/null
@@ -1,83 +0,0 @@
-@ignore @report=false
-Feature: Mock Backend
-
- Scenario: Setup Mock-backend
- * def newBuffer =
- """
- function() {
- var Queue = Java.type("common.net.MessageQueue")
- return new Queue()
- }
- """
- * def getBackend =
- """
- function() {
- var Backend = Java.type("fe.net.MockBackend")
- return new Backend(newBuffer(), port)
- }
- """
-
- * def backend = call getBackend
-
- * def getRollCallVerification =
- """
- function(){
- var RollCallVerification = Java.type("fe.utils.verification.RollCallVerification")
- return new RollCallVerification()
- }
- """
- * def rollCallVerification = call getRollCallVerification
-
- * def getMessageVerification =
- """
- function (){
- var MessageVerification = Java.type("fe.utils.verification.MessageVerification")
- return new MessageVerification()
- }
- """
-
- * def messageVerification = call getMessageVerification
-
- * def getVerificationUtils =
- """
- function(){
- var VerificationUtils = Java.type("fe.utils.verification.VerificationUtils")
- return new VerificationUtils()
- }
- """
-
- * def verificationUtils = call getVerificationUtils
-
- * def getElectionVerification =
- """
- function (){
- var ElectionVerification = Java.type("fe.utils.verification.ElectionVerification")
- return new ElectionVerification()
- }
- """
- * def electionVerification = call getElectionVerification
-
- * def getConstants =
- """
- function(){
- var Consants = Java.type("common.utils.Constants")
- return new Consants()
- }
- """
- * def wait =
- """
- function(secs) {
- java.lang.Thread.sleep(secs*1000)
- }
- """
-
- * def constants = call getConstants
-
- * karate.log('Backend started at ', backend.getPort())
- * def buffer = backend.getBuffer()
-
- * def stopBackend = function() { backend.stop() }
-
- # Shutdown backend automatically after the end of a scenario and feature
- * configure afterScenario = stopBackend
- * configure afterFeature = stopBackend
diff --git a/tests/karate/src/test/java/fe/utils/android.feature b/tests/karate/src/test/java/fe/utils/android.feature
index b09c9fa1c0..355e00f9fd 100644
--- a/tests/karate/src/test/java/fe/utils/android.feature
+++ b/tests/karate/src/test/java/fe/utils/android.feature
@@ -1,172 +1,11 @@
@ignore @report=false
Feature: android page object
-
- Background: Android Preset
- * configure driver = { type: 'android', webDriverPath : "/wd/hub", start: false, httpConfig : { readTimeout: 120000 }}
-
- * def capabilities = android.desiredConfig
- # Replace the relative path to apk the absolute path
- * capabilities.app = karate.toAbsolutePath('file:' + capabilities.app)
- * def driverOptions = { webDriverSession: { desiredCapabilities : "#(capabilities)" } }
-
- # ================= Page Object Start ====================
-
- # Tab buttons
- * def tab_home_selector = '#com.github.dedis.popstellar:id/home_home_menu'
- * def tab_connect_selector = '#com.github.dedis.popstellar:id/home_connect_menu'
- * def launch_selector = '#com.github.dedis.popstellar:id/home_launch_menu'
- * def tab_wallet_selector = '#com.github.dedis.popstellar:id/home_wallet_menu'
-
- # Launch tab
- * def tab_launch_lao_name_selector = '#com.github.dedis.popstellar:id/entry_box_launch'
- * def tab_launch_create_lao_selector = '#com.github.dedis.popstellar:id/button_launch'
-
- # Wallet tab
- * def tab_wallet_new_wallet_selector = '#com.github.dedis.popstellar:id/button_new_wallet'
- * def tab_wallet_confirm_selector = '#com.github.dedis.popstellar:id/button_confirm_seed'
-
- # Lao Event List
- * def add_event_selector = '#com.github.dedis.popstellar:id/add_event'
- * def add_roll_call_selector = '#com.github.dedis.popstellar:id/add_roll_call'
- * def roll_call_title_selector = '#com.github.dedis.popstellar:id/roll_call_title_text'
- * def roll_call_open_selector = '#com.github.dedis.popstellar:id/roll_call_open'
- * def roll_call_confirm_selector = '#com.github.dedis.popstellar:id/roll_call_confirm'
- * def roll_call_close_confirm_selector = '#com.github.dedis.popstellar:id/add_attendee_confirm'
- * def event_name_selector = '#com.github.dedis.popstellar:id/event_card_text_view'
-
- # Roll Call Screen
- * def roll_call_action_selector = '#com.github.dedis.popstellar:id/roll_call_management_button'
- * def roll_call_close_selector = '#com.github.dedis.popstellar:id/add_attendee_confirm'
- * def roll_call_manual_selector = '#com.github.dedis.popstellar:id/permission_manual_rc'
- * def allow_camera_selector = '#com.github.dedis.popstellar:id/allow_camera_button'
- * def manual_add_text_selector = '#com.github.dedis.popstellar:id/manual_add_edit_text'
- * def manual_add_confirm_selector = '#com.github.dedis.popstellar:id/manual_add_confirm'
-
- # Election Screen
- * def add_election_selector = '#com.github.dedis.popstellar:id/add_election'
- * def election_name_selector = '#com.github.dedis.popstellar:id/election_setup_name'
- * def election_question_selector = '#com.github.dedis.popstellar:id/election_question'
- * def election_confirm_selector = '#com.github.dedis.popstellar:id/election_submit_button'
- * def election_ballot_selector_1 = '#com.github.dedis.popstellar:id/new_ballot_option_text'
- # This relies on the fact that the ballot 1 has already been modified with an input,
- # which leaves the second ballot option the only one with the hint text
- * def election_ballot_selector_2 = '//*[@text="ballot option"]'
- * def election_management_selector = '#com.github.dedis.popstellar:id/election_management_button'
- * def election_action_selector = '#com.github.dedis.popstellar:id/election_action_button'
-
- # Cast vote screen
- * def cast_vote_ballot_selector_2 = '//*[@text="choice 2"]'
- * def cast_vote_button_selector = '#com.github.dedis.popstellar:id/cast_vote_button'
-
- @name=basic_setup
- Scenario: Setup connection to the backend and complete wallet initialization
- Given driver driverOptions
-
- # Create and import mock backend
- * call read('classpath:fe/net/mockBackend.feature')
- * def backendURL = 'ws://10.0.2.2:' + backend.getPort()
- # Import message filters
- * call read('classpath:common/net/filters.feature')
-
- # As the settings tab does not have an id, this is how we click on it.
- # If this breaks, use this code to log the page hierarchy :
- # karate.log(driver.getHttp().path("source").get().value)
- And click('//*[@content-desc="More options"]')
- * click('#com.github.dedis.popstellar:id/title')
-
- # Input the mock backend url and connect to it
- * input('#com.github.dedis.popstellar:id/entry_box_server_url', backendURL)
- * click('#com.github.dedis.popstellar:id/button_apply')
- * match backend.waitForConnection(5000) == true
-
- # Initialize wallet
- * click(tab_wallet_selector)
- * click(tab_wallet_new_wallet_selector)
- * click(tab_wallet_confirm_selector)
- * dialog(true)
-
- * retry(5,1000).click(launch_selector)
-
- # Roll call create android procedure
- @name=create_roll_call
- Scenario: Creates a roll call for an already created LAO
- When click(add_event_selector)
- And click(add_roll_call_selector)
-
- # Provide roll call information
- And input(roll_call_title_selector, constants.RC_NAME)
-
- # Roll call open android procedure
- @name=open_roll_call
- Scenario: Opens the created roll-call
- * click(event_name_selector)
- * backend.clearBuffer()
- * click(roll_call_action_selector)
-
- @name=close_roll_call
- Scenario: Closes a roll call with only the organizer attending
- # Close roll call
- * retry(5,200).click(roll_call_close_selector)
- * backend.clearBuffer()
- * dialog(true)
-
-
- @name=close_roll_call_w_attendees
- Scenario: Closes a roll call with 2 attendees and the organizer
- # Add attendees
- * input(manual_add_text_selector, token1)
- * click(manual_add_confirm_selector)
- * input(manual_add_text_selector, token2)
- * click(manual_add_confirm_selector)
- * backend.clearBuffer()
-
- # wait for popup
- * wait(3)
- # Close roll call
- * click(roll_call_close_selector)
- * dialog(true)
-
- # Roll call open android procedure
- @name=reopen_roll_call
- Scenario: reopens the created roll-call
- * click(event_name_selector)
- * backend.clearBuffer()
- * click(roll_call_action_selector)
-
- # Election setup android procedure
- @name=setup_election
- Scenario: Create election with 1 question and 2 ballots
- * retry(5, 1000).click(add_event_selector)
- * click(add_election_selector)
- * input(election_name_selector, constants.ELECTION_NAME)
- * input(election_question_selector, constants.QUESTION_CONTENT)
- * input(election_ballot_selector_1, constants.BALLOT_1)
- * input(election_ballot_selector_2, constants.BALLOT_2)
- * backend.clearBuffer()
- * click(election_confirm_selector)
-
- # Election open android procedure
- @name=open_election
- Scenario: Open election
- * click(event_name_selector)
- * backend.clearBuffer()
- * click(election_management_selector)
- * retry(5,1000).dialog(true)
- * wait(1)
-
- # Election cast vote android procedure
- @name=cast_vote
- Scenario: Cast a vote for the second ballot
- * click(election_action_selector)
- * click(cast_vote_ballot_selector_2)
- * backend.clearBuffer()
- * click(cast_vote_button_selector)
- * wait(5)
-
- @name=end_election
- Scenario: End an election
- * click(event_name_selector)
- * click(election_management_selector)
- * backend.clearBuffer()
- * retry(5,1000).dialog(true)
- * wait(1)
+ Background:
+ # Wallet screen
+ * def wallet_button_empty_ok = '//*[@text="OK"]'
+ * def wallet_seed_wallet_text = '#com.github.dedis.popstellar:id/seed_wallet_text'
+
+ @name=open_app
+ Scenario:
+ Given driver webDriverOptions
+ Then waitFor(wallet_button_empty_ok).click()
diff --git a/tests/karate/src/test/java/fe/utils/constants.feature b/tests/karate/src/test/java/fe/utils/constants.feature
new file mode 100644
index 0000000000..f78a8e0661
--- /dev/null
+++ b/tests/karate/src/test/java/fe/utils/constants.feature
@@ -0,0 +1,5 @@
+@ignore @report=false
+Feature: Constants
+ Scenario: Creates constants that will be used by other features
+ * def PLATFORM_FEATURES = 'classpath:fe/utils/platform.feature'
+ * def OPEN_APP = 'open_app'
diff --git a/tests/karate/src/test/java/fe/utils/platform.feature b/tests/karate/src/test/java/fe/utils/platform.feature
new file mode 100644
index 0000000000..b66e80a9df
--- /dev/null
+++ b/tests/karate/src/test/java/fe/utils/platform.feature
@@ -0,0 +1,7 @@
+@ignore @report=false
+Feature: current env page object
+ Scenario:
+ * def page_object = 'classpath:fe/utils/.feature@name='
+ * replace page_object.env = karate.env
+ * replace page_object.name = name
+ * call read(page_object)
diff --git a/tests/karate/src/test/java/fe/utils/simpleScenarios.feature b/tests/karate/src/test/java/fe/utils/simpleScenarios.feature
deleted file mode 100644
index e5cf8d2fca..0000000000
--- a/tests/karate/src/test/java/fe/utils/simpleScenarios.feature
+++ /dev/null
@@ -1,60 +0,0 @@
-@ignore @report=false @env=android,web
-Feature: Simple Scenarios
-
- @name=basic_setup
- Scenario: Basic setup
- * def page_object = 'classpath:fe/utils/.feature@name=basic_setup'
- * replace page_object.env = karate.env
- * call read(page_object)
-
- @name=create_lao
- Scenario: Create a LAO send the right messages to the backend
- Given call read('classpath:fe/utils/simpleScenarios.feature@name=basic_setup')
- * backend.setLaoCreateMode()
- * input(tab_launch_lao_name_selector, 'Lao Name')
- And click(tab_launch_create_lao_selector)
-
- @name=create_roll_call
- Scenario: Create a roll-call and everything needed before
- Given call read('classpath:fe/utils/simpleScenarios.feature@name=create_lao')
- * def rc_page_object = 'classpath:fe/utils/.feature@name=create_roll_call'
- * replace rc_page_object.env = karate.env
- * call read(rc_page_object)
- * backend.clearBuffer()
- * backend.setValidBroadcastMode()
- And click(roll_call_confirm_selector)
-
- @name=open_roll_call
- Scenario: Open a roll call
- Given call read('classpath:fe/utils/simpleScenarios.feature@name=create_roll_call')
- * def rc_page_object = 'classpath:fe/utils/.feature@name=open_roll_call'
- * replace rc_page_object.env = karate.env
- * call read(rc_page_object)
-
- @name=close_roll_call
- Scenario: Close a roll-call
- Given call read('classpath:fe/utils/simpleScenarios.feature@name=open_roll_call')
- * def rc_page_object = 'classpath:fe/utils/.feature@name=close_roll_call'
- * replace rc_page_object.env = karate.env
- * call read(rc_page_object)
-
- @name=setup_election
- Scenario: Setup an election
- Given call read('classpath:fe/utils/simpleScenarios.feature@name=close_roll_call')
- * def election_page_object = 'classpath:fe/utils/.feature@name=setup_election'
- * replace election_page_object.env = karate.env
- * call read(election_page_object)
-
- @name=open_election
- Scenario: Setup an election
- Given call read('classpath:fe/utils/simpleScenarios.feature@name=setup_election')
- * def election_page_object = 'classpath:fe/utils/.feature@name=open_election'
- * replace election_page_object.env = karate.env
- * call read(election_page_object)
-
- @name=cast_vote
- Scenario: Setup an election
- Given call read('classpath:fe/utils/simpleScenarios.feature@name=open_election')
- * def election_page_object = 'classpath:fe/utils/.feature@name=cast_vote'
- * replace election_page_object.env = karate.env
- * call read(election_page_object)
diff --git a/tests/karate/src/test/java/fe/utils/verification/ElectionVerification.java b/tests/karate/src/test/java/fe/utils/verification/ElectionVerification.java
deleted file mode 100644
index daebf34aa6..0000000000
--- a/tests/karate/src/test/java/fe/utils/verification/ElectionVerification.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package fe.utils.verification;
-
-import be.utils.Hash;
-import com.intuit.karate.Json;
-import com.intuit.karate.Logger;
-import common.utils.Constants;
-
-import java.security.NoSuchAlgorithmException;
-import java.util.List;
-
-import static common.utils.Constants.*;
-import static fe.utils.verification.VerificationUtils.getMsgDataJson;
-import static fe.utils.verification.VerificationUtils.getStringFromIntegerField;
-
-/** This class contains functions used to test fields specific to Roll-Call */
-public class ElectionVerification {
- private static final Logger logger = new Logger(ElectionVerification.class.getSimpleName());
- private Constants constants = new Constants();
-
- /**
- * Verifies that the election id is coherently computed
- * @param message the network message
- * @return true if the computed election id matches what is expected
- */
- public boolean verifyElectionId(String message) {
- Json setupMessageJson = getMsgDataJson(message);
-
- String electionId = setupMessageJson.get(ID);
- String createdAt = getStringFromIntegerField(setupMessageJson, CREATED_AT);
- String laoId = setupMessageJson.get(LAO);
- String electionName = setupMessageJson.get(NAME);
-
- try {
- return electionId.equals(
- Hash.hash(
- "Election".getBytes(),
- laoId.getBytes(),
- createdAt.getBytes(),
- electionName.getBytes()));
- } catch (NoSuchAlgorithmException e) {
- logger.info("verification failed with error: " + e);
- return false;
- }
- }
-
- /**
- * Verifies that the question id is coherently computed
- * @param message the network message
- * @return true if the computed question id matches what is expected
- */
- public boolean verifyQuestionId(String message){
- Json setupMessageJson = getMsgDataJson(message);
- Json questionJson = getElectionQuestion(message);
-
- String electionId = setupMessageJson.get(ID);
- String questionId = questionJson.get(ID);
- String question = questionJson.get(QUESTION);
-
- try {
- return questionId.equals(
- Hash.hash(
- "Question".getBytes(),
- electionId.getBytes(),
- question.getBytes()));
- } catch (NoSuchAlgorithmException e) {
- logger.info("verification failed with error: " + e);
- return false;
- }
- }
-
- /**
- * Verifies that the vote id is coherently computed
- * @param message the network message
- * @param index the index of the ballot that was selected
- * @return true if the computed question id matches what is expected
- */
- public boolean verifyVoteId(String message, int index){
- Json voteMessageJson = getMsgDataJson(message);
- Json voteJson = getVotes(message);
-
- String electionId = voteMessageJson.get(constants.ELECTION);
- String questionId = voteJson.get(QUESTION);
- String voteId = voteJson.get(ID);
- String vote = getStringFromIntegerField(voteJson, VOTE);
-
- try {
- return voteId.equals(
- Hash.hash(
- "Vote".getBytes(),
- electionId.getBytes(),
- questionId.getBytes(),
- vote.getBytes()));
- } catch (NoSuchAlgorithmException e) {
- logger.info("verification failed with error: " + e);
- return false;
- }
- }
-
- public String getQuestionContent(String message){
- Json questionJson = getElectionQuestion(message);
- return questionJson.get(QUESTION);
- }
-
- public String getVotingMethod(String message){
- Json questionJson = getElectionQuestion(message);
- return questionJson.get(VOTING_METHOD);
- }
-
- public String getBallotOption(String message, int index){
- Json questionJson = getElectionQuestion(message);
- List ballots = questionJson.get(BALLOT_OPTIONS);
- return ballots.get(index);
- }
-
- /**
- * Gets the vote field
- * @param message an element of the "votes" field array of a cast vote message
- * @return the "vote" field of the message in argument
- */
- public String getVote(String message){
- Json votes = getVotes(message);
- return getStringFromIntegerField(votes, VOTE);
- }
-
- private Json getElectionQuestion(String message){
- Json setupMessageJson = getMsgDataJson(message);
- List questionArray = setupMessageJson.get(QUESTIONS);
- return Json.of(questionArray.get(0));
- }
-
- /**
- * gets the first element of the "votes" field of a cast vote network message
- * @param message the network message
- * @return the first element of the "votes" field of a cast vote network message
- */
- private Json getVotes(String message){
- Json msgJson = getMsgDataJson(message);
- List questionArray = msgJson.get(VOTES);
- return Json.of(questionArray.get(0));
- }
-}
diff --git a/tests/karate/src/test/java/fe/utils/verification/MessageVerification.java b/tests/karate/src/test/java/fe/utils/verification/MessageVerification.java
deleted file mode 100644
index 2081d8bfb2..0000000000
--- a/tests/karate/src/test/java/fe/utils/verification/MessageVerification.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package fe.utils.verification;
-
-import be.utils.Hash;
-import com.google.crypto.tink.PublicKeyVerify;
-import com.google.crypto.tink.subtle.Ed25519Verify;
-import com.intuit.karate.Json;
-import com.intuit.karate.Logger;
-
-import java.security.GeneralSecurityException;
-import java.security.NoSuchAlgorithmException;
-
-import static common.utils.Constants.*;
-import static common.utils.Base64Utils.convertB64URLToByteArray;
-
-/**
- * This class contains functions useful to test message fields for several kind of high level messages
- */
-public class MessageVerification {
- private final static Logger logger = new Logger(MessageVerification.class.getSimpleName());
-
- /**
- * Verify the message_id of a network message
- * @param message the network message
- * @return true if the computed message_id matches the one provided in Json
- */
- public boolean verifyMessageIdField(String message) {
- Json messageFieldJson = VerificationUtils.getMessageFieldFromMessage(message);
-
- String data = messageFieldJson.get(DATA);
- String signature = messageFieldJson.get(SIGNATURE);
- String msgId = messageFieldJson.get(MESSAGE_ID);
- try {
- return msgId.equals(Hash.hash(data.getBytes(), signature.getBytes()));
- } catch (NoSuchAlgorithmException e) {
- logger.info("verification failed with error: " + e);
- return false;
- }
- }
-
- /**
- * Verify the signature of a network message
- * @param message the "message" field of the network message
- * @return true if signature field of the message matches the sender and data
- */
- public boolean verifyMessageSignature(String message) {
- Json messageFieldJson = VerificationUtils.getMessageFieldFromMessage(message);
-
- String senderB64 = messageFieldJson.get(SENDER);
- String signatureB64 = messageFieldJson.get(SIGNATURE);
- String dataB64 = messageFieldJson.get(DATA);
-
- byte[] sender = convertB64URLToByteArray(senderB64);
- byte[] signature = convertB64URLToByteArray(signatureB64);
- byte[] data = convertB64URLToByteArray(dataB64);
-
- PublicKeyVerify verify = new Ed25519Verify(sender);
-
- try {
- verify.verify(signature, data);
- return true;
- } catch (GeneralSecurityException e) {
- logger.info("verification failed with error: " + e);
- return false;
- }
- }
-}
diff --git a/tests/karate/src/test/java/fe/utils/verification/RollCallVerification.java b/tests/karate/src/test/java/fe/utils/verification/RollCallVerification.java
deleted file mode 100644
index e790fdd5c7..0000000000
--- a/tests/karate/src/test/java/fe/utils/verification/RollCallVerification.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package fe.utils.verification;
-
-import be.utils.Hash;
-import com.intuit.karate.Json;
-import com.intuit.karate.Logger;
-
-import java.security.NoSuchAlgorithmException;
-import java.util.List;
-
-import static common.utils.Constants.*;
-import static common.utils.JsonUtils.getJSON;
-import static fe.utils.verification.VerificationUtils.getMsgDataJson;
-import static fe.utils.verification.VerificationUtils.getStringFromIntegerField;
-
-/** This class contains functions used to test fields specific to Roll-Call */
-public class RollCallVerification {
- private static final Logger logger = new Logger(RollCallVerification.class.getSimpleName());
-
- /**
- * Verifies that the roll call id is computed as expected
- *
- * @param message the message sent over the network
- * @return true if the roll call id field matches expectations
- */
- public boolean verifyRollCallId(String message) {
- String laoId = getLaoId(message);
- Json createMessageJson = getMsgDataJson(message);
-
- return verifyRollCallId(createMessageJson, laoId);
- }
-
- /**
- * Verifies that the roll call open update_id field is valid
- *
- * @param message the message sent over the network
- * @return true if the update_id field match expectations
- */
- public boolean verifyRollCallUpdateId(String message, String action) {
- String laoId = getLaoId(message);
- Json msgDataJson = getMsgDataJson(message);
- return verifyUpdateId(msgDataJson, laoId, action);
- }
-
- /**
- * Verifies the presence of the attendees in the network message and that the number of attendees
- * implies the presence of the organizer
- *
- * @param message the network message
- * @param attendees the attendees added
- * @return true if every specified attendees is in the message and if the number of attendees in
- * the message = number of specified attendees + 1 (for the organizer)
- */
- public boolean verifyAttendeesPresence(String message, String... attendees) {
- Json msgDataJson = getMsgDataJson(message);
- List msgAttendees = msgDataJson.get(ATTENDEES);
- logger.info("Nbr attendees " + attendees.length + " message " + msgAttendees.toString());
- for (String attendee : attendees) {
- if (!msgAttendees.contains(attendee)) {
- return false;
- }
- }
- // The attendee list should be the organizer and all added attendees
- return attendees.length + 1 == msgAttendees.size();
- }
-
- ////////////////////// Utils //////////////////////
-
- private boolean verifyRollCallId(Json createMessageJson, String laoId) {
- String rcId = createMessageJson.get(ID);
- String creation = getStringFromIntegerField(createMessageJson, CREATION);
- String rcName = createMessageJson.get(NAME);
-
- try {
- return rcId.equals(
- Hash.hash(
- "R".getBytes(), laoId.getBytes(), creation.getBytes(), rcName.getBytes()));
- } catch (NoSuchAlgorithmException e) {
- logger.info("verification failed with error: " + e);
- return false;
- }
- }
-
- private String getLaoId(String message) {
- Json paramsFieldJson = getJSON(Json.of(message), PARAMS);
- String channel = paramsFieldJson.get(CHANNEL);
-
- // The laoId is the channel without leading /root/ and end \ characters
- return channel.replace("/root/", "").replace("\\", "");
- }
-
- /**
- * Verifies the update_id of a roll-call message
- *
- * @param rollCallMessageJson the message_data of the roll-call message
- * @param laoId the laoId of the LAO in which the roll-call is taking place
- * @param action the roll call action (e.g. open)
- * @return true if the computed id matches the one provided in the message_data
- */
- private boolean verifyUpdateId(Json rollCallMessageJson, String laoId, String action) {
- String referenceKey = action.equals(CLOSE_STATIC) ? CLOSES : OPENS;
- String timeKey = action.equals(CLOSE_STATIC) ? CLOSED_AT : OPENED_AT;
- String updateId = rollCallMessageJson.get(UPDATE_ID);
- String reference = rollCallMessageJson.get(referenceKey);
- String time = getStringFromIntegerField(rollCallMessageJson, timeKey);
-
- try {
- return updateId.equals(
- Hash.hash(
- "R".getBytes(), laoId.getBytes(), reference.getBytes(), time.getBytes()));
- } catch (NoSuchAlgorithmException e) {
- logger.info("verification failed with error: " + e);
- return false;
- }
- }
-}
diff --git a/tests/karate/src/test/java/fe/utils/verification/VerificationUtils.java b/tests/karate/src/test/java/fe/utils/verification/VerificationUtils.java
deleted file mode 100644
index d9b670021c..0000000000
--- a/tests/karate/src/test/java/fe/utils/verification/VerificationUtils.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package fe.utils.verification;
-
-import com.intuit.karate.Json;
-import common.utils.Base64Utils;
-import common.utils.JsonUtils;
-
-import static common.utils.Constants.*;
-
-/**
- * This class contains useful utils functions for the verification process
- */
-public class VerificationUtils {
-
- /**
- * Returns the Json object containing the "message" field of the provided network message
- * @param message the network message
- * @return the Json object containing the "message" field
- */
- public static Json getMessageFieldFromMessage(String message){
- Json jsonMessage = Json.of(message);
- Json paramFieldJson = JsonUtils.getJSON(jsonMessage, PARAMS);
- return Json.of(paramFieldJson.get(MESSAGE));
- }
-
- /**
- * Returns the "data" field of the provided network message
- * @param message the network message
- * @return the "data" field in base64 format
- */
- public static String getDataFieldFromMessage(String message){
- Json messageField = getMessageFieldFromMessage(message);
- return messageField.get(DATA);
- }
-
- /**
- * Returns the Json object containing the decoded "data" field of the provided network message
- * @param message the network message
- * @return the Json object containing the decoded "data" field
- */
- public static Json getMsgDataJson(String message){
- String b64Data = getDataFieldFromMessage(message);
- String data = new String(Base64Utils.convertB64URLToByteArray(b64Data));
- return Json.of(data);
- }
-
- public String getObject(String message){
- Json data = getMsgDataJson(message);
- return data.get(OBJECT);
- }
-
- public String getAction(String message){
- Json data = getMsgDataJson(message);
- return data.get(ACTION);
- }
-
- public String getVersion(String message){
- Json data = getMsgDataJson(message);
- return data.get(VERSION);
- }
-
- public String getName(String message){
- Json data = getMsgDataJson(message);
- return data.get(NAME);
- }
-
- /** Because of internal type used by karate, doing casting in 2 steps is required */
- public static String getStringFromIntegerField(Json json, String key) {
- Integer intTemp = json.get(key);
- return String.valueOf(intTemp);
- }
-}
diff --git a/tests/karate/src/test/java/fe/utils/web.feature b/tests/karate/src/test/java/fe/utils/web.feature
index 69dd176a99..0935e23a4e 100644
--- a/tests/karate/src/test/java/fe/utils/web.feature
+++ b/tests/karate/src/test/java/fe/utils/web.feature
@@ -1,205 +1,13 @@
@ignore @report=false
-Feature: web test
-
- Background: App Preset
- * configure driver = { type: 'chrome', executable: 'C:/Program Files/Google/Chrome/Application/chrome.exe'}
- #* configure driver = { type: 'chrome' }
- * def driverOptions = karate.toAbsolutePath('file:../../fe1-web/web-build/index.html')
-
- # ================= Page Object Start ====================
-
- # Introduction screen
- * def exploring_selector = "[data-testid='exploring_selector']"
-
- #Home Screen
- * def tab_connect_selector = '{}Connect'
- * def launch_selector = "[data-testid='launch_selector']"
-
- # Launch screen
- * def tab_launch_lao_name_selector = "input[data-testid='launch_organization_name_selector']"
- * def backend_address_selector = "input[data-testid='launch_address_selector']"
- * def tab_launch_create_lao_selector = "[data-testid='launch_launch_selector']"
-
- # Lao Event List
- * def past_header_selector = '{^}Past'
- * def add_event_selector = "[data-testid='create_event_selector']"
- * def tab_events_selector = '{}Events'
- * def roll_call_title_selector = "input[data-testid='roll_call_name_selector']"
- * def roll_call_location_selector = "input[data-testid='roll_call_location_selector']"
- * def roll_call_confirm_selector = "[data-testid='roll_call_confirm_selector']"
- * def event_name_selector = "[data-testid='current_event_selector_0']"
-
- # Roll Call Screen
- * def roll_call_option_selector = "[data-testid='roll_call_options']"
- * def roll_call_stop_scanning_selector = "[data-testid='roll_call_open_stop_scanning']"
- * def roll_call_manual_selector = "[data-testid='roll_call_open_add_manually']"
- * def manual_add_description_selector = '{^}Enter token:'
- * def manual_add_confirm_selector = '{}Add'
- * def manual_add_done_selector = '{}Done'
-
- # Election
- * def election_name_selector = "[data-testid='election_name_selector']"
- * def election_question_selector = "[data-testid='question_selector_0']"
- * def election_ballot_selector_1 = "[data-testid='question_0_ballots_option_0_input']"
- * def election_ballot_selector_2 = "[data-testid='question_0_ballots_option_1_input']"
- * def election_confirm_selector = "[data-testid='election_confirm_selector']"
- * def election_event_selector = "[data-testid='current_event_selector_0']"
- * def election_option_selector = "[data-testid='election_option_selector']"
- * def election_opened_option_selector = "[data-testid='election_opened_option_selector']"
-
- # Cast vote screen
- * def cast_vote_button_selector = "[data-testid='election_vote_selector']"
- * def cast_vote_ballot_selector_2 = "[data-testid='questions_0_ballots_option_1_checkbox']"
-
- @name=basic_setup
- Scenario: Setup connection to the backend and complete on the home page
- Given driver driverOptions
-
- # Create and import mock backend
- And call read('classpath:fe/net/mockBackend.feature')
- * def backendURL = 'ws://localhost:' + backend.getPort()
- # Import message filters
- And call read('classpath:common/net/filters.feature')
-
- # The default input function is not consistent and does not work every time.
- # This replaces the input function with one that just tries again until it works.
- * def input =
- """
- function(selector, data) {
- tries = 0
- while (driver.attribute(selector, "value") != data) {
- if (tries++ >= max_input_retry)
- throw "Could not input " + data + " - max number of retry reached."
- driver.clear(selector)
- driver.input(selector, data)
- delay(10)
- }
- }
- """
-
- * click(exploring_selector)
- # Click on the connect navigation item
- * retry(5,1000).click(tab_connect_selector)
- # Click on launch button
- * click(launch_selector)
- # Connect to the backend
- * input(backend_address_selector, backendURL)
-
- # Roll call create web procedure
- @name=create_roll_call
- Scenario: Creates a roll call for an already created LAO
- Given retry(10, 200).click(tab_events_selector)
- And click(add_event_selector)
-
- # Clicking on Create Roll-Call. This is because it is (as of now) an actionSheet element which does not have an id
- # If it breaks down, check that the name of the button has not changed, try to add more delay. Otherwise maybe karate
- # added a way to directly do that after the time of our writing.
- #
- # script allows the evaluation of arbitrary javascript code and document.evaluate
- # (https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate) allows the evaluation of an XPath expression.
- #
- # Somehow this turned out to work, at least if it was wrapped
- # in a setTimeout which delays the execution of the script.
- # The XPath selector is described here: https://stackoverflow.com/a/29289196/2897827
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Create Roll-Call\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 1000)")
-
- # Provide roll call required information
- And retry(5, 1000).input(roll_call_title_selector, constants.RC_NAME)
- And input(roll_call_location_selector, 'EPFL')
-
- # Roll call open web procedure
- @name=open_roll_call
- Scenario: Opens the created roll-call
- * retry(5,1000).click(event_name_selector)
- * retry(5,1000).click(roll_call_option_selector)
- * backend.clearBuffer()
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Open Roll-Call\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 500)")
-
- @name=close_roll_call
- Scenario: Closes a roll call with only the organizer attending
- * wait(1)
- * retry(5,1000).click(roll_call_option_selector)
- # We need to start scanning for the organizer token to be added
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Scan Attendees\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 1000)")
- * retry(5,1000).click(roll_call_stop_scanning_selector)
- * backend.clearBuffer()
- * click(roll_call_option_selector)
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Close Roll-Call\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 900)")
- # needed to work
- * wait(2)
-
- @name=close_roll_call_w_attendees
- Scenario: Closes a roll call with 2 attendees and the organizer
- * wait(1)
- * retry(5,1000).click(roll_call_option_selector)
- # We need to start scanning for the organizer token to be added
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Scan Attendees\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 1000)")
- * retry(5,1000).click(roll_call_manual_selector)
-
- # Add attendees
- * below(manual_add_description_selector).input(token1)
- * click(manual_add_confirm_selector)
- * below(manual_add_description_selector).clear()
- * below(manual_add_description_selector).input(token2)
- * click(manual_add_confirm_selector)
- * click(manual_add_done_selector)
- * retry(5,1000).click(roll_call_stop_scanning_selector)
- * backend.clearBuffer()
- * click(roll_call_option_selector)
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Close Roll-Call\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 1000)")
- # needed to work
- * wait(2)
-
- @name=reopen_roll_call
- Scenario: Reopen a closed roll call
- * click(past_header_selector)
- * retry(5,1000).click(event_name_selector)
- * wait(1)
- * click(roll_call_option_selector)
- * backend.clearBuffer()
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Re-open Roll-Call\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 1000)")
- * wait(2)
-
- # Election setup web procedure
- @name=setup_election
- Scenario: Create election with 1 question and 2 ballots
- And click(add_event_selector)
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Create Election\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 1000)")
- * wait(1)
- * retry(5, 1000).input(election_name_selector, constants.ELECTION_NAME)
- * input(election_question_selector, constants.QUESTION_CONTENT)
- * input(election_ballot_selector_1, constants.BALLOT_1)
- * input(election_ballot_selector_2, constants.BALLOT_2)
- * backend.clearBuffer()
- * click(election_confirm_selector)
-
- # Election open web procedure
- @name=open_election
- Scenario: Open election
- * retry(5,1000).click(election_event_selector)
- * retry(5,1000).click(election_option_selector)
- * backend.clearBuffer()
- * script("setTimeout(() => document.evaluate('//div[text()=\\'Open Election\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 1000)")
- * wait(2)
-
- # Election cast vote web procedure
- @name=cast_vote
- Scenario: Cast a vote for the second ballot
- * wait(1)
- # Click on second ballot checkbox
- * click(cast_vote_ballot_selector_2)
- * wait(1)
- * backend.clearBuffer()
- * click(cast_vote_button_selector)
- * wait(1)
-
-
- # Election end web procedure
- @name=end_election
- Scenario: End an election
- * wait(1)
- * retry(5,1000).click(election_opened_option_selector)
- * script("setTimeout(() => document.evaluate('//div[text()=\\'End Election and Tally Votes\\']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click(), 1000)")
- * backend.clearBuffer()
- * wait(2)
-
+Feature: web page object
+ Background:
+ # Wallet screen
+ * def wallet_seed_wallet_text = "[data-testid='seed_wallet_text']"
+
+ @name=open_app
+ Scenario:
+ Given driver webDriverOptions
+ Given driver 'about:blank'
+ And driver.dimensions = { left: 0, top: 0, width: screenWidth, height: screenHeight }
+ Then driver frontendURL
+ And delay(1000)
diff --git a/tests/karate/src/test/java/karate-config.js b/tests/karate/src/test/java/karate-config.js
index 542bca7b2f..65090d4374 100644
--- a/tests/karate/src/test/java/karate-config.js
+++ b/tests/karate/src/test/java/karate-config.js
@@ -32,27 +32,88 @@ function fn() {
config.backendPath = 'server';
config.frontendWsURL = `ws://${config.host}:${config.frontendPort}/${config.frontendPath}`;
config.backendWsURL = `ws://${config.host}:${config.backendPort}/${config.backendPath}`;
- } else {
- config.port = 9005;
- config.timeout = 1000;
+ } else if (env === 'web') {
+ config.frontendURL = karate.properties['url'] || `file://${karate.toAbsolutePath('file:../../fe1-web/web-build/index.html')}`;
+ config.screenWidth = karate.properties['screenWidth'] || 1920;
+ config.screenHeight = karate.properties['screenHeight'] || 1080;
- if (env === 'web') {
- config.max_input_retry = 10;
- } else if (env === 'android') {
- const android = {};
- android["desiredConfig"] = {
- "app" : "../../fe2-android/app/build/outputs/apk/debug/app-debug.apk",
- "newCommandTimeout" : 1000,
- "platformVersion" : "9.0",
- "platformName" : "Android",
- "connectHardwareKeyboard" : true,
- "deviceName" : "emulator-5554",
- "avd" : "Pixel_4_API_30",
- "automationName" : "UiAutomator2",
- "autoGrantPermissions" : true
- }
- config["android"] = android
+ let platform = karate.properties['platform'] || karate.os.type;
+ if (platform === 'macosx') {
+ platform = 'mac';
}
+
+ const browser = karate.properties['browser'] || 'chrome';
+ const browserOptions = {
+ chrome: {
+ type: 'chromedriver',
+ capabilities: {
+ alwaysMatch: {
+ 'platformName': platform,
+ 'appium:automationName': 'Chromium',
+ 'browserName': 'chrome'
+ }
+ }
+ },
+ edge: {
+ type: 'chromedriver',
+ capabilities: {
+ alwaysMatch: {
+ 'platformName': platform,
+ 'appium:automationName': 'Chromium',
+ 'browserName': 'MicrosoftEdge'
+ }
+ }
+ },
+ firefox: {
+ type: 'geckodriver',
+ capabilities: {
+ alwaysMatch: {
+ 'platformName': platform,
+ 'appium:automationName': 'Gecko',
+ 'browserName': 'firefox'
+ }
+ }
+ },
+ safari: {
+ type: 'safaridriver',
+ capabilities: {
+ alwaysMatch: {
+ 'platformName': platform,
+ 'appium:automationName': 'Safari',
+ 'browserName': 'safari'
+ }
+ }
+ }
+ };
+
+ const { type, capabilities } = browserOptions[browser];
+
+
+ karate.configure('driver', { type, port: 4723, webDriverPath : "/", start: false });
+ config.webDriverOptions = {
+ webDriverSession: {
+ capabilities,
+ desiredCapabilities: {}
+ }
+ };
+ } else if (env === 'android') {
+ karate.configure('driver', { type: 'android', webDriverPath : "/", start: false });
+ const app = karate.properties['app'] || '../../fe2-android/app/build/outputs/apk/debug/app-debug.apk';
+ config.webDriverOptions = {
+ webDriverSession: {
+ capabilities: {
+ alwaysMatch: {
+ 'appium:platformName': 'Android',
+ 'appium:automationName': 'uiautomator2',
+ 'appium:app': `${karate.toAbsolutePath(`file:${app}`)}`,
+ 'appium:autoGrantPermissions': true,
+ 'appium:avd': karate.properties['avd'],
+ }
+ },
+ desiredCapabilities: {
+ }
+ }
+ };
}
return config;