chromePrefs = new HashMap<>();
+ chromePrefs.put("download.default_directory", downloadPath);
+ chromePrefs.put("profile.default_content_settings.popups", 0);
ChromeOptions options = new ChromeOptions();
+ options.setExperimentalOption("prefs", chromePrefs);
options.addArguments("--allow-file-access-from-files");
+ if (TestProperties.isDevServer()) {
+ options.addArguments("incognito");
+ } else {
+ // Get user data from browser to bypass google blocking automated log in.
+ // Log in manually to teammates to use that log in data for e2e tests.
+ if (TestProperties.CHROME_USER_DATA_PATH.isEmpty()
+ || !Files.exists(Paths.get(TestProperties.CHROME_USER_DATA_PATH))) {
+ throw new RuntimeException("Chrome user data path not found. Failed to create webdriver.");
+ }
+ options.addArguments("user-data-dir=" + TestProperties.CHROME_USER_DATA_PATH);
+ }
+
return new ChromeDriver(options);
}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/BrowserPool.java b/src/e2e/java/teammates/e2e/pageobjects/BrowserPool.java
deleted file mode 100644
index 7c0e9b4b23b..00000000000
--- a/src/e2e/java/teammates/e2e/pageobjects/BrowserPool.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package teammates.e2e.pageobjects;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Manage the pool of {@link Browser} instances.
- * This class is thread-safe.
- */
-public final class BrowserPool {
-
- // This class is implemented as a Singleton class.
- // The reason we're not implementing this class as static because we want to use wait() and notify().
-
- /**
- * Maximum number of browsers in the pool.
- *
- * Ideally, should be equal to the number of threads used for testing.
- */
- private static final int CAPACITY = System.getenv("CI") == null ? 9 + 1 : 2;
- // +1 in case a sequential ui test uses a browser other than the first in pool
-
- private static BrowserPool instance;
- private List pool;
-
- private BrowserPool() {
- pool = new ArrayList<>(CAPACITY);
- }
-
- private static BrowserPool getInstance() {
- synchronized (BrowserPool.class) {
- if (instance == null) {
- instance = new BrowserPool();
- }
- return instance;
- }
- }
-
- /**
- * Returns a Browser object ready to be used.
- */
- public static Browser getBrowser() {
- return getInstance().requestInstance();
- }
-
- /**
- * Releases a Browser instance back to the pool, ready to be reused.
- */
- public static void release(Browser browser) {
- BrowserPool pool = getInstance();
- // Synchronized to ensure thread-safety
- synchronized (pool) {
- browser.isInUse = false;
- pool.notifyAll();
- }
- }
-
- private Browser requestInstance() {
- while (true) {
- // Synchronized to ensure thread-safety
- synchronized (this) {
- // Look for instantiated and available object.
- for (Browser browser : pool) {
- if (!browser.isInUse) {
- browser.isInUse = true;
- return browser;
- }
- }
-
- // If less than capacity, create new object
- if (pool.size() < CAPACITY) {
- Browser browser = new Browser();
- browser.isInUse = true;
- pool.add(browser);
- return browser;
- }
-
- // Wait if no more free objects and no more capacity.
- try {
- wait(500);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
-}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/CourseJoinConfirmationPage.java b/src/e2e/java/teammates/e2e/pageobjects/CourseJoinConfirmationPage.java
new file mode 100644
index 00000000000..eb12a509047
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/CourseJoinConfirmationPage.java
@@ -0,0 +1,34 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * Page Object Model for the course join confirmation page.
+ */
+public class CourseJoinConfirmationPage extends AppPage {
+ @FindBy(id = "btn-confirm")
+ private WebElement confirmButton;
+
+ public CourseJoinConfirmationPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ public boolean containsExpectedPageContents() {
+ return waitForElementPresence(By.tagName("h3")).getText().contains("Confirm your Google account");
+ }
+
+ public void verifyJoiningUser(String googleId) {
+ assertEquals(browser.driver.findElement(By.id("user-id")).getText(), googleId);
+ }
+
+ public T confirmJoinCourse(Class typeOfPage) {
+ click(confirmButton);
+ waitForPageToLoad();
+ return changePageType(typeOfPage);
+ }
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/DevServerLoginPage.java b/src/e2e/java/teammates/e2e/pageobjects/DevServerLoginPage.java
index 965da92e9b6..182add39193 100644
--- a/src/e2e/java/teammates/e2e/pageobjects/DevServerLoginPage.java
+++ b/src/e2e/java/teammates/e2e/pageobjects/DevServerLoginPage.java
@@ -33,7 +33,7 @@ public void loginAsAdmin(String adminUsername, String adminPassword) {
fillTextBox(emailTextBox, adminUsername);
click(isAdminCheckBox);
click(loginButton);
- waitForElementVisibility(By.tagName("h1"));
+ waitForPageToLoad();
browser.isAdminLoggedIn = true;
}
@@ -46,7 +46,7 @@ public StudentHomePage loginAsStudent(String username, String password) {
public T loginAsStudent(String username, String password, Class typeOfPage) {
fillTextBox(emailTextBox, username);
click(loginButton);
- waitForElementVisibility(By.tagName("h1"));
+ waitForPageToLoad();
browser.isAdminLoggedIn = false;
return changePageType(typeOfPage);
}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/ErrorReportingModal.java b/src/e2e/java/teammates/e2e/pageobjects/ErrorReportingModal.java
new file mode 100644
index 00000000000..0dbd67eba73
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/ErrorReportingModal.java
@@ -0,0 +1,25 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+
+import org.openqa.selenium.By;
+
+/**
+ * Page Object Model for the error reporting modal.
+ */
+public class ErrorReportingModal extends AppPage {
+
+ public ErrorReportingModal(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ public boolean containsExpectedPageContents() {
+ return waitForElementPresence(By.tagName("h2")).getText().contains("Uh oh! Something went wrong.");
+ }
+
+ public void verifyErrorMessage(String message) {
+ assertEquals(browser.driver.findElement(By.id("error-message")).getText(),
+ "The server returns the following error message: \"" + message + "\".");
+ }
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java b/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java
new file mode 100644
index 00000000000..8b3c4d717ec
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java
@@ -0,0 +1,894 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.TimeoutException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.FeedbackParticipantType;
+import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes;
+import teammates.common.datatransfer.attributes.FeedbackResponseAttributes;
+import teammates.common.datatransfer.attributes.FeedbackSessionAttributes;
+import teammates.common.datatransfer.questions.FeedbackConstantSumQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackConstantSumResponseDetails;
+import teammates.common.datatransfer.questions.FeedbackContributionQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackContributionResponseDetails;
+import teammates.common.datatransfer.questions.FeedbackMcqQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackMcqResponseDetails;
+import teammates.common.datatransfer.questions.FeedbackMsqQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackMsqResponseDetails;
+import teammates.common.datatransfer.questions.FeedbackNumericalScaleQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackNumericalScaleResponseDetails;
+import teammates.common.datatransfer.questions.FeedbackRankOptionsQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackRankOptionsResponseDetails;
+import teammates.common.datatransfer.questions.FeedbackRankQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackRankRecipientsResponseDetails;
+import teammates.common.datatransfer.questions.FeedbackRubricQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackRubricResponseDetails;
+import teammates.common.datatransfer.questions.FeedbackTextQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackTextResponseDetails;
+import teammates.common.util.Const;
+
+/**
+ * Represents the feedback submission page of the website.
+ */
+public class FeedbackSubmitPage extends AppPage {
+
+ @FindBy(id = "confirmation-email-checkbox")
+ private WebElement confirmationEmailCheckbox;
+
+ public FeedbackSubmitPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ if (isElementPresent(By.className("modal-content"))) {
+ waitForConfirmationModalAndClickOk();
+ }
+ reloadPageIfStuckLoading();
+ return getPageTitle().contains("Submit Feedback");
+ }
+
+ public void verifyFeedbackSessionDetails(FeedbackSessionAttributes feedbackSession) {
+ assertEquals(getCourseId(), feedbackSession.getCourseId());
+ assertEquals(getFeedbackSessionName(), feedbackSession.getFeedbackSessionName());
+ assertDateEquals(getOpeningTime(), feedbackSession.getStartTime(), feedbackSession.getTimeZone());
+ assertDateEquals(getClosingTime(), feedbackSession.getEndTime(), feedbackSession.getTimeZone());
+ assertEquals(getInstructions(), feedbackSession.getInstructions());
+ }
+
+ public void verifyNumQuestions(int expected) {
+ assertEquals(browser.driver.findElements(By.id("question-submission-form")).size(), expected);
+ }
+
+ public void verifyQuestionDetails(int qnNumber, FeedbackQuestionAttributes questionAttributes) {
+ assertEquals(getQuestionBrief(qnNumber), questionAttributes.getQuestionDetails().getQuestionText());
+ verifyVisibilityList(qnNumber, questionAttributes);
+ if (questionAttributes.getQuestionDescription() != null) {
+ assertEquals(getQuestionDescription(qnNumber), questionAttributes.getQuestionDescription());
+ }
+ }
+
+ public void verifyLimitedRecipients(int qnNumber, int numRecipients, List recipientNames) {
+ List recipientDropdowns = getQuestionForm(qnNumber).findElements(By.id("recipient-dropdown"));
+ assertEquals(numRecipients, recipientDropdowns.size());
+ List recipients = recipientDropdowns.get(0).findElements(By.tagName("option"));
+ assertEquals(recipientNames.size(), recipients.size() - 1);
+ Collections.sort(recipientNames);
+ for (int i = 0; i < recipientNames.size(); i++) {
+ assertEquals(recipientNames.get(i), recipients.get(i + 1).getText());
+ }
+ }
+
+ public void verifyRecipients(int qnNumber, List recipientNames, String role) {
+ WebElement questionForm = getQuestionForm(qnNumber);
+ Collections.sort(recipientNames);
+ for (int i = 0; i < recipientNames.size(); i++) {
+ assertEquals(recipientNames.get(i) + " (" + role + ")",
+ questionForm.findElement(By.id("recipient-name-" + i)).getText());
+ }
+ }
+
+ public void verifyWarningMessageForPartialResponse(int[] unansweredQuestions) {
+ click(getSubmitButton());
+ StringBuilder expectedSb = new StringBuilder();
+ for (int unansweredQuestion : unansweredQuestions) {
+ expectedSb.append(unansweredQuestion).append(", ");
+ }
+ String expectedString = expectedSb.toString().substring(0, expectedSb.length() - 2) + ".";
+ String warningString = waitForElementPresence(By.id("not-answered-questions")).getText();
+ assertEquals(warningString.split(": ")[1], expectedString);
+ waitForConfirmationModalAndClickOk();
+ }
+
+ public void verifyCannotSubmit() {
+ assertFalse(getSubmitButton().isEnabled());
+ }
+
+ public void markWithConfirmationEmail() {
+ markOptionAsSelected(confirmationEmailCheckbox);
+ }
+
+ public void addComment(int qnNumber, String recipient, String newComment) {
+ WebElement commentSection = getCommentSection(qnNumber, recipient);
+ click(commentSection.findElement(By.id("btn-add-comment")));
+ writeToCommentEditor(commentSection, newComment);
+ clickSubmitButton();
+ }
+
+ public void editComment(int qnNumber, String recipient, String editedComment) {
+ WebElement commentSection = getCommentSection(qnNumber, recipient);
+ click(commentSection.findElement(By.id("btn-edit-comment")));
+ writeToCommentEditor(commentSection, editedComment);
+ clickSubmitButton();
+ }
+
+ public void deleteComment(int qnNumber, String recipient) {
+ clickAndConfirm(getCommentSection(qnNumber, recipient).findElement(By.id("btn-delete-comment")));
+ }
+
+ public void verifyComment(int qnNumber, String recipient, String expectedComment) {
+ WebElement commentSection = getCommentSection(qnNumber, recipient);
+ String actualComment = commentSection.findElement(By.id("comment-text")).getAttribute("innerHTML");
+ assertEquals(expectedComment, actualComment);
+ }
+
+ public void verifyNoCommentPresent(int qnNumber, String recipient) {
+ int numComments = getCommentSection(qnNumber, recipient).findElements(By.id("comment-text")).size();
+ assertEquals(numComments, 0);
+ }
+
+ public void verifyTextQuestion(int qnNumber, FeedbackTextQuestionDetails questionDetails) {
+ String recommendedLengthText = getQuestionForm(qnNumber).findElement(By.id("recommended-length")).getText();
+ assertEquals(recommendedLengthText, "Recommended length for the answer: "
+ + questionDetails.getRecommendedLength() + " words");
+ }
+
+ public void submitTextResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackTextResponseDetails responseDetails = (FeedbackTextResponseDetails) response.getResponseDetails();
+ writeToRichTextEditor(getTextResponseEditor(qnNumber, recipient), responseDetails.getAnswer());
+ clickSubmitButton();
+ }
+
+ public void verifyTextResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackTextResponseDetails responseDetails = (FeedbackTextResponseDetails) response.getResponseDetails();
+ int responseLength = responseDetails.getAnswer().split(" ").length;
+ assertEquals(getEditorRichText(getTextResponseEditor(qnNumber, recipient)), responseDetails.getAnswer());
+ assertEquals(getResponseLengthText(qnNumber, recipient), "Response length: " + responseLength
+ + " words");
+ }
+
+ public void verifyMcqQuestion(int qnNumber, String recipient, FeedbackMcqQuestionDetails questionDetails) {
+ List mcqChoices = questionDetails.getMcqChoices();
+ List optionTexts = getMcqOptions(qnNumber, recipient);
+
+ for (int i = 0; i < mcqChoices.size(); i++) {
+ assertEquals(mcqChoices.get(i), optionTexts.get(i).getText());
+ }
+
+ if (questionDetails.isOtherEnabled()) {
+ assertEquals("Other", getMcqSection(qnNumber, recipient).findElement(By.id("other-option")).getText());
+ }
+ }
+
+ public void verifyGeneratedMcqQuestion(int qnNumber, String recipient, List options) {
+ List optionTexts = getMcqOptions(qnNumber, recipient);
+ for (int i = 0; i < options.size(); i++) {
+ assertEquals(options.get(i), optionTexts.get(i).getText());
+ }
+ }
+
+ public void submitMcqResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackMcqResponseDetails responseDetails = (FeedbackMcqResponseDetails) response.getResponseDetails();
+ if (responseDetails.isOther()) {
+ markOptionAsSelected(getMcqOtherOptionRadioBtn(qnNumber, recipient));
+ fillTextBox(getMcqOtherOptionTextbox(qnNumber, recipient), responseDetails.getOtherFieldContent());
+ } else {
+ List optionTexts = getMcqOptions(qnNumber, recipient);
+ for (int i = 0; i < optionTexts.size(); i++) {
+ if (optionTexts.get(i).getText().equals(responseDetails.getAnswer())) {
+ markOptionAsSelected(getMcqRadioBtns(qnNumber, recipient).get(i));
+ break;
+ }
+ }
+ }
+ clickSubmitButton();
+ }
+
+ public void verifyMcqResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackMcqResponseDetails responseDetails = (FeedbackMcqResponseDetails) response.getResponseDetails();
+ if (responseDetails.isOther()) {
+ assertTrue(getMcqOtherOptionRadioBtn(qnNumber, recipient).isSelected());
+ assertEquals(getMcqOtherOptionTextbox(qnNumber, recipient).getAttribute("value"),
+ responseDetails.getOtherFieldContent());
+ } else {
+ List optionTexts = getMcqOptions(qnNumber, recipient);
+ List radioBtns = getMcqRadioBtns(qnNumber, recipient);
+ for (int i = 0; i < optionTexts.size(); i++) {
+ if (optionTexts.get(i).getText().equals(responseDetails.getAnswer())) {
+ assertTrue(radioBtns.get(i).isSelected());
+ break;
+ }
+ assertFalse(radioBtns.get(i).isSelected());
+ }
+ }
+ }
+
+ public void verifyMsqQuestion(int qnNumber, String recipient, FeedbackMsqQuestionDetails questionDetails) {
+ List msqChoices = questionDetails.getMsqChoices();
+ if (questionDetails.isOtherEnabled()) {
+ msqChoices.add("Other");
+ }
+ if (questionDetails.getMinSelectableChoices() == Integer.MIN_VALUE) {
+ msqChoices.add("None of the above");
+ }
+ List optionTexts = getMsqOptions(qnNumber, recipient);
+ for (int i = 0; i < msqChoices.size(); i++) {
+ assertEquals(msqChoices.get(i), optionTexts.get(i).getText());
+ }
+ verifyMsqSelectableOptionsMessage(qnNumber, questionDetails);
+ }
+
+ private void verifyMsqSelectableOptionsMessage(int qnNumber, FeedbackMsqQuestionDetails questionDetails) {
+ if (questionDetails.getMinSelectableChoices() > Integer.MIN_VALUE) {
+ assertEquals(getQuestionForm(qnNumber).findElement(By.id("min-options-message")).getText(),
+ "Choose at least " + questionDetails.getMinSelectableChoices() + " options.");
+ }
+ if (questionDetails.getMaxSelectableChoices() > Integer.MIN_VALUE) {
+ assertEquals(getQuestionForm(qnNumber).findElement(By.id("max-options-message")).getText(),
+ "Choose no more than " + questionDetails.getMaxSelectableChoices() + " options.");
+ }
+ }
+
+ public void verifyGeneratedMsqQuestion(int qnNumber, String recipient, FeedbackMsqQuestionDetails questionDetails,
+ List options) {
+ List optionTexts = getMsqOptions(qnNumber, recipient);
+ for (int i = 0; i < options.size(); i++) {
+ assertEquals(options.get(i), optionTexts.get(i).getText());
+ }
+ verifyMsqSelectableOptionsMessage(qnNumber, questionDetails);
+ }
+
+ public void submitMsqResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackMsqResponseDetails responseDetails = (FeedbackMsqResponseDetails) response.getResponseDetails();
+ List answers = responseDetails.getAnswers();
+ if (answers.get(0).isEmpty()) {
+ answers.add("None of the above");
+ }
+ List optionTexts = getMsqOptions(qnNumber, recipient);
+ List checkboxes = getMsqCheckboxes(qnNumber, recipient);
+ for (int i = 0; i < optionTexts.size(); i++) {
+ if (answers.contains(optionTexts.get(i).getText())) {
+ markOptionAsSelected(checkboxes.get(i));
+ } else {
+ markOptionAsUnselected(checkboxes.get(i));
+ }
+ }
+ if (responseDetails.isOther()) {
+ markOptionAsSelected(getMsqOtherOptionCheckbox(qnNumber, recipient));
+ fillTextBox(getMsqOtherOptionTextbox(qnNumber, recipient), responseDetails.getOtherFieldContent());
+ }
+ clickSubmitButton();
+ }
+
+ public void verifyMsqResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackMsqResponseDetails responseDetails = (FeedbackMsqResponseDetails) response.getResponseDetails();
+ List answers = responseDetails.getAnswers();
+ if (answers.get(0).isEmpty()) {
+ answers.add("None of the above");
+ }
+ List optionTexts = getMsqOptions(qnNumber, recipient);
+ List checkboxes = getMsqCheckboxes(qnNumber, recipient);
+ for (int i = 0; i < optionTexts.size(); i++) {
+ if (answers.contains(optionTexts.get(i).getText())) {
+ assertTrue(checkboxes.get(i).isSelected());
+ } else if (optionTexts.get(i).getText().equals("Other")) {
+ assertEquals(checkboxes.get(i).isSelected(), responseDetails.isOther());
+ } else {
+ assertFalse(checkboxes.get(i).isSelected());
+ }
+ }
+ if (responseDetails.isOther()) {
+ assertEquals(getMsqOtherOptionTextbox(qnNumber, recipient).getAttribute("value"),
+ responseDetails.getOtherFieldContent());
+ }
+ }
+
+ public void verifyNumScaleQuestion(int qnNumber, String recipient,
+ FeedbackNumericalScaleQuestionDetails questionDetails) {
+ double step = questionDetails.getStep();
+ double twoSteps = 2 * step;
+ double min = questionDetails.getMinScale();
+ double max = questionDetails.getMaxScale();
+ String possibleValues = String.format("Possible values: [%s, %s, %s, ..., %s, %s, %s]",
+ getDoubleString(min), getDoubleString(min + step), getDoubleString(min + twoSteps),
+ getDoubleString(max - twoSteps), getDoubleString(max - step), getDoubleString(max));
+ String actualValues = getNumScaleSection(qnNumber, recipient).findElement(By.id("possible-values")).getText();
+ assertEquals(actualValues, possibleValues);
+ }
+
+ public void submitNumScaleResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackNumericalScaleResponseDetails responseDetails =
+ (FeedbackNumericalScaleResponseDetails) response.getResponseDetails();
+ fillTextBox(getNumScaleInput(qnNumber, recipient), Double.toString(responseDetails.getAnswer()));
+ clickSubmitButton();
+ }
+
+ public void verifyNumScaleResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackNumericalScaleResponseDetails responseDetails =
+ (FeedbackNumericalScaleResponseDetails) response.getResponseDetails();
+ assertEquals(getNumScaleInput(qnNumber, recipient).getAttribute("value"),
+ getDoubleString(responseDetails.getAnswer()));
+ }
+
+ public void verifyConstSumQuestion(int qnNumber, String recipient,
+ FeedbackConstantSumQuestionDetails questionDetails) {
+ if (!questionDetails.isDistributeToRecipients()) {
+ List constSumOptions = questionDetails.getConstSumOptions();
+ List optionTexts = getConstSumOptions(qnNumber, recipient);
+ for (int i = 0; i < constSumOptions.size(); i++) {
+ assertEquals(constSumOptions.get(i), optionTexts.get(i).getText());
+ }
+ }
+
+ int totalPoints = questionDetails.getPoints();
+ if (questionDetails.isPointsPerOption()) {
+ totalPoints *= questionDetails.getNumOfConstSumOptions();
+ }
+ assertEquals(getQuestionForm(qnNumber).findElement(By.id("total-points-message")).getText(),
+ "Total points distributed should add up to " + totalPoints + ".");
+
+ if (questionDetails.isForceUnevenDistribution()) {
+ String entityType = questionDetails.isDistributeToRecipients() ? "recipient" : "option";
+ if (questionDetails.getDistributePointsFor().equals("All options")) {
+ assertEquals(getQuestionForm(qnNumber).findElement(By.id("all-uneven-message")).getText(),
+ "Every " + entityType + " should be allocated different number of points.");
+ } else {
+ assertEquals(getQuestionForm(qnNumber).findElement(By.id("one-uneven-message")).getText(),
+ "At least one " + entityType + " should be allocated different number of points.");
+ }
+ }
+ }
+
+ public void submitConstSumOptionResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackConstantSumResponseDetails responseDetails =
+ (FeedbackConstantSumResponseDetails) response.getResponseDetails();
+ List answers = responseDetails.getAnswers();
+ List constSumInputs = getConstSumInputs(qnNumber, recipient);
+ for (int i = 0; i < answers.size(); i++) {
+ fillTextBox(constSumInputs.get(i), Integer.toString(answers.get(i)));
+ }
+ clickSubmitButton();
+ }
+
+ public void verifyConstSumOptionResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackConstantSumResponseDetails responseDetails =
+ (FeedbackConstantSumResponseDetails) response.getResponseDetails();
+ List answers = responseDetails.getAnswers();
+ List constSumInputs = getConstSumInputs(qnNumber, recipient);
+ for (int i = 0; i < answers.size(); i++) {
+ assertEquals(constSumInputs.get(i).getAttribute("value"), Integer.toString(answers.get(i)));
+ }
+ }
+
+ public void submitConstSumRecipientResponse(int qnNumber, List responses) {
+ List recipientInputs = getConstSumRecipientInputs(qnNumber);
+ for (int i = 0; i < responses.size(); i++) {
+ FeedbackConstantSumResponseDetails response =
+ (FeedbackConstantSumResponseDetails) responses.get(i).getResponseDetails();
+ fillTextBox(recipientInputs.get(i), Integer.toString(response.getAnswers().get(0)));
+ }
+ clickSubmitButton();
+ }
+
+ public void verifyConstSumRecipientResponse(int qnNumber, List responses) {
+ List recipientInputs = getConstSumRecipientInputs(qnNumber);
+ for (int i = 0; i < responses.size(); i++) {
+ FeedbackConstantSumResponseDetails response =
+ (FeedbackConstantSumResponseDetails) responses.get(i).getResponseDetails();
+ assertEquals(recipientInputs.get(i).getAttribute("value"),
+ Integer.toString(response.getAnswers().get(0)));
+ }
+ }
+
+ public void verifyContributionQuestion(int qnNumber, FeedbackContributionQuestionDetails questionDetails) {
+ try {
+ selectDropdownOptionByText(getContributionDropdowns(qnNumber).get(0), "Not Sure");
+ assertTrue(questionDetails.isNotSureAllowed());
+ } catch (NoSuchElementException e) {
+ assertFalse(questionDetails.isNotSureAllowed());
+ }
+ }
+
+ public void submitContributionResponse(int qnNumber, List responses) {
+ List dropdowns = getContributionDropdowns(qnNumber);
+ for (int i = 0; i < responses.size(); i++) {
+ FeedbackContributionResponseDetails response =
+ (FeedbackContributionResponseDetails) responses.get(i).getResponseDetails();
+ selectDropdownOptionByText(dropdowns.get(i), getContributionString(response.getAnswer()));
+ }
+ clickSubmitButton();
+ }
+
+ public void verifyContributionResponse(int qnNumber, List responses) {
+ List dropdowns = getContributionDropdowns(qnNumber);
+ for (int i = 0; i < responses.size(); i++) {
+ FeedbackContributionResponseDetails response =
+ (FeedbackContributionResponseDetails) responses.get(i).getResponseDetails();
+ assertEquals(getSelectedDropdownOptionText(dropdowns.get(i)), getContributionString(response.getAnswer()));
+ }
+ }
+
+ public void verifyRubricQuestion(int qnNumber, String recipient, FeedbackRubricQuestionDetails questionDetails) {
+ List choices = questionDetails.getRubricChoices();
+ List subQuestions = questionDetails.getRubricSubQuestions();
+ List> descriptions = questionDetails.getRubricDescriptions();
+
+ String[][] expectedTable = new String[subQuestions.size() + 1][choices.size() + 1];
+ expectedTable[0][0] = "";
+ for (int i = 1; i <= choices.size(); i++) {
+ expectedTable[0][i] = choices.get(i - 1);
+ }
+ for (int i = 1; i <= subQuestions.size(); i++) {
+ expectedTable[i][0] = subQuestions.get(i - 1);
+ }
+ for (int i = 1; i <= descriptions.size(); i++) {
+ List description = descriptions.get(i - 1);
+ for (int j = 1; j <= description.size(); j++) {
+ expectedTable[i][j] = description.get(j - 1);
+ }
+ }
+ verifyTableBodyValues(getRubricTable(qnNumber, recipient), expectedTable);
+ }
+
+ public void submitRubricResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackRubricResponseDetails responseDetails =
+ (FeedbackRubricResponseDetails) response.getResponseDetails();
+ List answers = responseDetails.getAnswer();
+ for (int i = 0; i < answers.size(); i++) {
+ click(getRubricInputs(qnNumber, recipient, i + 2).get(answers.get(i)));
+ }
+ clickSubmitButton();
+ }
+
+ public void verifyRubricResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackRubricResponseDetails responseDetails =
+ (FeedbackRubricResponseDetails) response.getResponseDetails();
+ List answers = responseDetails.getAnswer();
+ for (int i = 0; i < answers.size(); i++) {
+ assertTrue(getRubricInputs(qnNumber, recipient, i + 2).get(answers.get(i)).isSelected());
+ }
+ }
+
+ public void verifyRankQuestion(int qnNumber, String recipient, FeedbackRankQuestionDetails questionDetails) {
+ if (questionDetails.getMaxOptionsToBeRanked() != Integer.MIN_VALUE) {
+ assertEquals(getQuestionForm(qnNumber).findElement(By.id("max-options-message")).getText(),
+ "Rank no more than " + questionDetails.getMaxOptionsToBeRanked() + " options.");
+ }
+ if (questionDetails.getMinOptionsToBeRanked() != Integer.MIN_VALUE) {
+ assertEquals(getQuestionForm(qnNumber).findElement(By.id("min-options-message")).getText(),
+ "Rank at least " + questionDetails.getMinOptionsToBeRanked() + " options.");
+ }
+ if (questionDetails instanceof FeedbackRankOptionsQuestionDetails) {
+ FeedbackRankOptionsQuestionDetails optionDetails = (FeedbackRankOptionsQuestionDetails) questionDetails;
+ List options = optionDetails.getOptions();
+ List optionTexts = getRankOptions(qnNumber, recipient);
+ for (int i = 0; i < options.size(); i++) {
+ assertEquals(options.get(i), optionTexts.get(i).getText());
+ }
+ }
+ }
+
+ public void submitRankOptionResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackRankOptionsResponseDetails responseDetails =
+ (FeedbackRankOptionsResponseDetails) response.getResponseDetails();
+ List answers = responseDetails.getAnswers();
+ for (int i = 0; i < answers.size(); i++) {
+ if (answers.get(i) == Const.POINTS_NOT_SUBMITTED) {
+ selectDropdownOptionByText(getRankOptionsDropdowns(qnNumber, recipient).get(i), "");
+ } else {
+ selectDropdownOptionByText(getRankOptionsDropdowns(qnNumber, recipient).get(i),
+ Integer.toString(answers.get(i)));
+ }
+ }
+ clickSubmitButton();
+ }
+
+ public void verifyRankOptionResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) {
+ FeedbackRankOptionsResponseDetails responseDetails =
+ (FeedbackRankOptionsResponseDetails) response.getResponseDetails();
+ List answers = responseDetails.getAnswers();
+ for (int i = 0; i < answers.size(); i++) {
+ if (answers.get(i) == Const.POINTS_NOT_SUBMITTED) {
+ assertEquals(getSelectedDropdownOptionText(getRankOptionsDropdowns(qnNumber, recipient).get(i)),
+ "");
+ } else {
+ assertEquals(getSelectedDropdownOptionText(getRankOptionsDropdowns(qnNumber, recipient).get(i)),
+ Integer.toString(answers.get(i)));
+ }
+ }
+ }
+
+ public void submitRankRecipientResponse(int qnNumber, List responses) {
+ List recipientDropdowns = getRankRecipientDropdowns(qnNumber);
+ for (int i = 0; i < responses.size(); i++) {
+ FeedbackRankRecipientsResponseDetails response =
+ (FeedbackRankRecipientsResponseDetails) responses.get(i).getResponseDetails();
+ if (response.getAnswer() == Const.POINTS_NOT_SUBMITTED) {
+ selectDropdownOptionByText(recipientDropdowns.get(i), "");
+ } else {
+ selectDropdownOptionByText(recipientDropdowns.get(i), Integer.toString(response.getAnswer()));
+ }
+ }
+ clickSubmitButton();
+ }
+
+ public void verifyRankRecipientResponse(int qnNumber, List responses) {
+ List recipientDropdowns = getRankRecipientDropdowns(qnNumber);
+ for (int i = 0; i < responses.size(); i++) {
+ FeedbackRankRecipientsResponseDetails response =
+ (FeedbackRankRecipientsResponseDetails) responses.get(i).getResponseDetails();
+ if (response.getAnswer() == Const.POINTS_NOT_SUBMITTED) {
+ assertEquals(getSelectedDropdownOptionText(recipientDropdowns.get(i)), "");
+ } else {
+ assertEquals(getSelectedDropdownOptionText(recipientDropdowns.get(i)),
+ Integer.toString(response.getAnswer()));
+ }
+ }
+ }
+
+ private String getCourseId() {
+ return browser.driver.findElement(By.id("course-id")).getText();
+ }
+
+ private String getFeedbackSessionName() {
+ return browser.driver.findElement(By.id("fs-name")).getText();
+ }
+
+ private String getOpeningTime() {
+ return browser.driver.findElement(By.id("opening-time")).getText();
+ }
+
+ private String getClosingTime() {
+ return browser.driver.findElement(By.id("closing-time")).getText();
+ }
+
+ private String getInstructions() {
+ return browser.driver.findElement(By.id("instructions")).getAttribute("innerHTML");
+ }
+
+ private void assertDateEquals(String actual, Instant instant, ZoneId timeZone) {
+ String dateStrWithAbbr = getDateStringWithAbbr(instant, timeZone);
+ String dateStrWithOffset = getDateStringWithOffset(instant, timeZone);
+
+ boolean isExpected = actual.equals(dateStrWithAbbr) || actual.equals(dateStrWithOffset);
+ assertTrue(isExpected);
+ }
+
+ private String getDateStringWithAbbr(Instant instant, ZoneId timeZone) {
+ return DateTimeFormatter
+ .ofPattern("EE, dd MMM, yyyy, hh:mm a z")
+ .format(instant.atZone(timeZone));
+ }
+
+ private String getDateStringWithOffset(Instant instant, ZoneId timeZone) {
+ return DateTimeFormatter
+ .ofPattern("EE, dd MMM, yyyy, hh:mm a X")
+ .format(instant.atZone(timeZone));
+ }
+
+ private WebElement getQuestionForm(int qnNumber) {
+ By questionFormId = By.id("question-submission-form");
+ waitForElementPresence(questionFormId);
+ return browser.driver.findElements(questionFormId).get(qnNumber - 1);
+ }
+
+ private String getQuestionBrief(int qnNumber) {
+ String questionDetails = getQuestionForm(qnNumber).findElement(By.id("question-details")).getText();
+ return questionDetails.split(": ")[1];
+ }
+
+ private void verifyVisibilityList(int qnNumber, FeedbackQuestionAttributes questionAttributes) {
+ if (questionAttributes.showResponsesTo.isEmpty()) {
+ verifyVisibilityStringPresent(qnNumber, "No-one can see your responses");
+ }
+ if (questionAttributes.recipientType.equals(FeedbackParticipantType.SELF)) {
+ verifyVisibilityStringPresent(qnNumber, "You can see your own feedback in the results page later on.");
+ }
+ for (FeedbackParticipantType viewerType : questionAttributes.showResponsesTo) {
+ verifyVisibilityStringPresent(qnNumber, getVisibilityString(questionAttributes, viewerType));
+ }
+ }
+
+ private void verifyVisibilityStringPresent(int qnNumber, String expectedString) {
+ List visibilityStrings = getQuestionForm(qnNumber).findElement(By.id("visibility-list"))
+ .findElements(By.tagName("li"));
+ for (WebElement visibilityString : visibilityStrings) {
+ if (visibilityString.getText().equals(expectedString)) {
+ return;
+ }
+ }
+ fail("Expected visibility string not found: " + qnNumber + ": " + expectedString);
+ }
+
+ private String getVisibilityString(FeedbackQuestionAttributes questionAttributes,
+ FeedbackParticipantType viewerType) {
+ if (!questionAttributes.showResponsesTo.contains(viewerType)) {
+ return "";
+ }
+
+ StringBuilder message = new StringBuilder(getViewerString(viewerType, questionAttributes.recipientType));
+ message.append(" can see your response");
+ if (questionAttributes.showRecipientNameTo.contains(viewerType)) {
+ message.append(", the name of the recipient");
+ if (questionAttributes.showGiverNameTo.contains(viewerType)) {
+ message.append(", and your name");
+ } else {
+ message.append(", but not your name");
+ }
+ } else {
+ if (questionAttributes.showGiverNameTo.contains(viewerType)) {
+ message.append(", and your name, but not the name of the recipient");
+ } else {
+ message.append(", but not the name of the recipient, or your name");
+ }
+ }
+ return message.toString();
+ }
+
+ private String getViewerString(FeedbackParticipantType viewerType, FeedbackParticipantType recipientType) {
+ switch(viewerType) {
+ case RECEIVER:
+ return "The receiving " + getRecipientString(recipientType);
+ case OWN_TEAM_MEMBERS:
+ return "Your team members";
+ case STUDENTS:
+ return "Other students in the course";
+ case INSTRUCTORS:
+ return "Instructors in this course";
+ default:
+ throw new RuntimeException("Unknown viewer type");
+ }
+ }
+
+ private String getRecipientString(FeedbackParticipantType recipientType) {
+ switch(recipientType) {
+ case TEAMS:
+ return "teams";
+ case OWN_TEAM_MEMBERS:
+ return "student";
+ case STUDENTS:
+ return "students";
+ case INSTRUCTORS:
+ return "instructors";
+ default:
+ throw new RuntimeException("Unknown recipientType");
+ }
+ }
+
+ private void clickSubmitButton() {
+ clickAndConfirm(getSubmitButton());
+ }
+
+ private void reloadPageIfStuckLoading() {
+ By loadingContainer = By.className("loading-container");
+ try {
+ if (isElementPresent(loadingContainer)) {
+ waitForElementStaleness(browser.driver.findElement(loadingContainer));
+ }
+ } catch (TimeoutException e) {
+ reloadPage();
+ }
+ }
+
+ private WebElement getSubmitButton() {
+ return waitForElementPresence(By.id("btn-submit"));
+ }
+
+ private String getQuestionDescription(int qnNumber) {
+ return getQuestionForm(qnNumber).findElement(By.id("question-description")).getAttribute("innerHTML");
+ }
+
+ private WebElement getCommentSection(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ return getQuestionForm(qnNumber).findElements(By.id("comment-section")).get(recipientIndex);
+ }
+
+ private void writeToCommentEditor(WebElement commentSection, String comment) {
+ scrollElementToCenter(commentSection);
+ waitForElementPresence(By.tagName("editor"));
+ writeToRichTextEditor(commentSection.findElement(By.tagName("editor")), comment);
+ }
+
+ private int getRecipientIndex(int qnNumber, String recipient) {
+ // For questions with recipient none or self.
+ if (recipient.isEmpty()) {
+ return 0;
+ }
+ WebElement questionForm = getQuestionForm(qnNumber);
+ // For questions with flexible recipient.
+ try {
+ List recipientDropdowns = questionForm.findElements(By.id("recipient-dropdown"));
+ for (int i = 0; i < recipientDropdowns.size(); i++) {
+ String dropdownText = getSelectedDropdownOptionText(recipientDropdowns.get(i));
+ if (dropdownText.isEmpty()) {
+ selectDropdownOptionByText(recipientDropdowns.get(i), recipient);
+ return i;
+ } else if (dropdownText.equals(recipient)) {
+ return i;
+ }
+ }
+ } catch (NoSuchElementException e) {
+ // continue
+ }
+ int i = 0;
+ while (true) {
+ if (questionForm.findElement(By.id("recipient-name-" + i)).getText().contains(recipient)) {
+ return i;
+ }
+ i++;
+ }
+ }
+
+ private WebElement getTextResponseEditor(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ WebElement questionForm = getQuestionForm(qnNumber);
+ WebElement editor = questionForm.findElements(By.tagName("tm-rich-text-editor")).get(recipientIndex);
+ scrollElementToCenter(editor);
+ return editor;
+ }
+
+ private String getResponseLengthText(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ return getQuestionForm(qnNumber).findElements(By.id("response-length")).get(recipientIndex).getText();
+ }
+
+ private String getDoubleString(Double value) {
+ return value % 1 == 0 ? Integer.toString(value.intValue()) : Double.toString(value);
+ }
+
+ private WebElement getMcqSection(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ WebElement questionForm = getQuestionForm(qnNumber);
+ return questionForm.findElements(By.tagName("tm-mcq-question-edit-answer-form")).get(recipientIndex);
+ }
+
+ private WebElement getMcqOtherOptionRadioBtn(int qnNumber, String recipient) {
+ WebElement mcqSection = getMcqSection(qnNumber, recipient);
+ return mcqSection.findElement(By.cssSelector("#other-option input[type=radio]"));
+ }
+
+ private WebElement getMcqOtherOptionTextbox(int qnNumber, String recipient) {
+ WebElement mcqSection = getMcqSection(qnNumber, recipient);
+ return mcqSection.findElement(By.cssSelector("#other-option input[type=text]"));
+ }
+
+ private List getMcqOptions(int qnNumber, String recipient) {
+ WebElement mcqSection = getMcqSection(qnNumber, recipient);
+ return mcqSection.findElements(By.className("option-text"));
+ }
+
+ private List getMcqRadioBtns(int qnNumber, String recipient) {
+ WebElement mcqSection = getMcqSection(qnNumber, recipient);
+ return mcqSection.findElements(By.cssSelector("input[type=radio]"));
+ }
+
+ private WebElement getMsqSection(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ WebElement questionForm = getQuestionForm(qnNumber);
+ return questionForm.findElements(By.tagName("tm-msq-question-edit-answer-form")).get(recipientIndex);
+ }
+
+ private WebElement getMsqOtherOptionCheckbox(int qnNumber, String recipient) {
+ WebElement msqSection = getMsqSection(qnNumber, recipient);
+ return msqSection.findElement(By.cssSelector("#other-option input[type=checkbox]"));
+ }
+
+ private WebElement getMsqOtherOptionTextbox(int qnNumber, String recipient) {
+ WebElement msqSection = getMsqSection(qnNumber, recipient);
+ return msqSection.findElement(By.cssSelector("#other-option input[type=text]"));
+ }
+
+ private List getMsqOptions(int qnNumber, String recipient) {
+ WebElement msqSection = getMsqSection(qnNumber, recipient);
+ return msqSection.findElements(By.tagName("strong"));
+ }
+
+ private List getMsqCheckboxes(int qnNumber, String recipient) {
+ WebElement msqSection = getMsqSection(qnNumber, recipient);
+ return msqSection.findElements(By.cssSelector("input[type=checkbox]"));
+ }
+
+ private WebElement getNumScaleSection(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ WebElement questionForm = getQuestionForm(qnNumber);
+ return questionForm.findElements(By.tagName("tm-num-scale-question-edit-answer-form")).get(recipientIndex);
+ }
+
+ private WebElement getNumScaleInput(int qnNumber, String recipient) {
+ WebElement numScaleSection = getNumScaleSection(qnNumber, recipient);
+ return numScaleSection.findElement(By.tagName("input"));
+ }
+
+ private WebElement getConstSumOptionsSection(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ WebElement questionForm = getQuestionForm(qnNumber);
+ return questionForm.findElements(By.tagName("tm-constsum-options-question-edit-answer-form")).get(recipientIndex);
+ }
+
+ private List getConstSumOptions(int qnNumber, String recipient) {
+ WebElement constSumOptionSection = getConstSumOptionsSection(qnNumber, recipient);
+ return constSumOptionSection.findElements(By.tagName("strong"));
+ }
+
+ private List getConstSumInputs(int qnNumber, String recipient) {
+ WebElement constSumOptionSection = getConstSumOptionsSection(qnNumber, recipient);
+ return constSumOptionSection.findElements(By.cssSelector("input[type=number]"));
+ }
+
+ private List getConstSumRecipientInputs(int qnNumber) {
+ return getQuestionForm(qnNumber).findElements(By.cssSelector("input[type=number]"));
+ }
+
+ private List getContributionDropdowns(int questionNum) {
+ return getQuestionForm(questionNum).findElements(By.tagName("select"));
+ }
+
+ private String getContributionString(int answer) {
+ if (answer == Const.POINTS_NOT_SURE) {
+ return "Not Sure";
+ } else if (answer == Const.POINTS_EQUAL_SHARE) {
+ return "Equal share";
+ } else {
+ return "Equal share" + (answer > 100 ? " + " : " - ") + Math.abs(answer - 100) + "%";
+ }
+ }
+
+ private WebElement getRubricSection(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ WebElement questionForm = getQuestionForm(qnNumber);
+ return questionForm.findElements(By.tagName("tm-rubric-question-edit-answer-form")).get(recipientIndex);
+ }
+
+ private WebElement getRubricTable(int qnNumber, String recipient) {
+ return getRubricSection(qnNumber, recipient).findElement(By.tagName("table"));
+ }
+
+ private List getRubricInputs(int qnNumber, String recipient, int rowNumber) {
+ WebElement rubricRow = getRubricSection(qnNumber, recipient).findElements(By.tagName("tr")).get(rowNumber - 1);
+ return rubricRow.findElements(By.tagName("input"));
+ }
+
+ private WebElement getRankOptionsSection(int qnNumber, String recipient) {
+ int recipientIndex = getRecipientIndex(qnNumber, recipient);
+ WebElement questionForm = getQuestionForm(qnNumber);
+ return questionForm.findElements(By.tagName("tm-rank-options-question-edit-answer-form")).get(recipientIndex);
+ }
+
+ private List getRankOptions(int questionNum, String recipient) {
+ WebElement rankSection = getRankOptionsSection(questionNum, recipient);
+ return rankSection.findElements(By.tagName("strong"));
+ }
+
+ private List getRankOptionsDropdowns(int questionNum, String recipient) {
+ WebElement rankSection = getRankOptionsSection(questionNum, recipient);
+ return rankSection.findElements(By.tagName("select"));
+ }
+
+ private List getRankRecipientDropdowns(int questionNum) {
+ return getQuestionForm(questionNum).findElements(By.tagName("select"));
+ }
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/GoogleLoginPage.java b/src/e2e/java/teammates/e2e/pageobjects/GoogleLoginPage.java
index b03856df8db..13f96da0ed0 100644
--- a/src/e2e/java/teammates/e2e/pageobjects/GoogleLoginPage.java
+++ b/src/e2e/java/teammates/e2e/pageobjects/GoogleLoginPage.java
@@ -1,38 +1,15 @@
package teammates.e2e.pageobjects;
-import org.openqa.selenium.By;
-import org.openqa.selenium.NoSuchElementException;
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
-import org.openqa.selenium.support.ui.ExpectedConditions;
-
-import com.google.common.base.Preconditions;
-
-import teammates.e2e.util.TestProperties;
-
/**
* Page Object Model for the official Google Accounts login page.
*/
public class GoogleLoginPage extends LoginPage {
private static final String EXPECTED_SNIPPET_SIGN_IN = "Sign in – Google accounts";
- private static final String EXPECTED_SNIPPET_APPROVAL = "requesting permission to access your Google Account";
-
- @FindBy(id = "initialView")
- private WebElement loginPanel;
-
- @FindBy(id = "identifierId")
- private WebElement identifierTextBox;
-
- @FindBy(id = "identifierNext")
- private WebElement identifierNextButton;
-
- @FindBy(css = "input[type=password]")
- private WebElement passwordTextBox;
-
- @FindBy(id = "passwordNext")
- private WebElement passwordNextButton;
+ private static final String ERROR_CANNOT_LOGIN = "Automated login not allowed on Google login page.";
+ // Google blocks log in by automation.
+ // This AppPage is for testing correct navigation and not for actual login.
public GoogleLoginPage(Browser browser) {
super(browser);
}
@@ -44,88 +21,16 @@ protected boolean containsExpectedPageContents() {
@Override
public void loginAsAdmin(String adminUsername, String adminPassword) {
- completeGoogleLoginSteps(adminUsername, adminPassword);
- browser.isAdminLoggedIn = true;
+ throw new RuntimeException(ERROR_CANNOT_LOGIN);
}
@Override
public StudentHomePage loginAsStudent(String username, String password) {
- return loginAsStudent(username, password, StudentHomePage.class);
+ throw new RuntimeException(ERROR_CANNOT_LOGIN);
}
@Override
public T loginAsStudent(String username, String password, Class typeOfPage) {
- completeGoogleLoginSteps(username, password);
- browser.isAdminLoggedIn = false;
- return changePageType(typeOfPage);
- }
-
- private void completeGoogleLoginSteps(String username, String password) {
- submitCredentials(username, password);
- dealWithSignIntoChromePage();
- handleApprovalPageIfAny();
- }
-
- private void dealWithSignIntoChromePage() {
- try {
- click(By.id("no-button"));
- waitForPageToLoad();
- } catch (NoSuchElementException e) {
- System.out.println("No 'sign into chrome' option");
- }
- }
-
- private void handleApprovalPageIfAny() {
- waitForPageToLoad();
- waitForRedirectIfAny();
- boolean isPageRequestingAccessApproval = getPageSource().contains(EXPECTED_SNIPPET_APPROVAL);
- if (isPageRequestingAccessApproval) {
- markCheckBoxAsChecked(browser.driver.findElement(By.id("persist_checkbox")));
- click(By.id("approve_button"));
- waitForPageToLoad();
- }
- }
-
- private void waitForRedirectIfAny() {
- String loginRedirectUrl = TestProperties.TEAMMATES_URL + "/_ah/conflogin";
- waitFor(d -> {
- String url = Preconditions.checkNotNull(d).getCurrentUrl();
- boolean isTeammatesPage = url.startsWith(TestProperties.TEAMMATES_URL) && !url.startsWith(loginRedirectUrl);
- boolean isApprovalPage = d.getPageSource().contains(EXPECTED_SNIPPET_APPROVAL);
- return isTeammatesPage || isApprovalPage;
- });
- }
-
- private void waitForLoginPanelAnimationToComplete() {
- // the login panel will have attribute `aria-busy="true"` while in animation
- waitFor(ExpectedConditions.attributeToBe(loginPanel, "aria-busy", ""));
- }
-
- private void submitCredentials(String username, String password) {
- completeFillIdentifierSteps(username);
- click(identifierNextButton);
-
- waitForLoginPanelAnimationToComplete();
- fillTextBox(passwordTextBox, password);
-
- click(passwordNextButton);
- waitForPageToLoad();
- }
-
- private void completeFillIdentifierSteps(String identifier) {
- By switchAccountButtonBy = By.cssSelector("div[aria-label='Switch account']");
- By useAnotherAccountButtonBy = By.xpath("//div[contains(text(), 'Use another account')]");
-
- if (isElementPresent(switchAccountButtonBy)) {
- click(switchAccountButtonBy);
- waitForLoginPanelAnimationToComplete();
- }
-
- if (isElementPresent(useAnotherAccountButtonBy)) {
- click(useAnotherAccountButtonBy);
- waitForLoginPanelAnimationToComplete();
- }
-
- fillTextBox(identifierTextBox, identifier);
+ throw new RuntimeException(ERROR_CANNOT_LOGIN);
}
}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseDetailsPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseDetailsPage.java
new file mode 100644
index 00000000000..75956fb5723
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseDetailsPage.java
@@ -0,0 +1,183 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import java.util.StringJoiner;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.datatransfer.attributes.InstructorAttributes;
+import teammates.common.datatransfer.attributes.StudentAttributes;
+import teammates.common.util.ThreadHelper;
+
+/**
+ * Represents the instructor course details page of the website.
+ */
+public class InstructorCourseDetailsPage extends AppPage {
+ @FindBy(id = "course-id")
+ private WebElement courseIdField;
+
+ @FindBy(id = "course-name")
+ private WebElement courseNameField;
+
+ @FindBy(id = "num-sections")
+ private WebElement numSectionsField;
+
+ @FindBy(id = "num-teams")
+ private WebElement numTeamsField;
+
+ @FindBy(id = "num-students")
+ private WebElement numStudentsField;
+
+ @FindBy(id = "instructors")
+ private WebElement instructorsField;
+
+ public InstructorCourseDetailsPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ return getPageTitle().contains("Course Details");
+ }
+
+ public void verifyCourseDetails(CourseAttributes course, InstructorAttributes[] instructors,
+ int numSections, int numTeams, int numStudents) {
+ assertEquals(course.getId(), courseIdField.getText());
+ assertEquals(course.getName(), courseNameField.getText());
+ assertEquals(Integer.toString(numSections), numSectionsField.getText());
+ assertEquals(Integer.toString(numTeams), numTeamsField.getText());
+ assertEquals(Integer.toString(numStudents), numStudentsField.getText());
+ assertEquals(getExpectedInstructorString(instructors), instructorsField.getText());
+ }
+
+ public void verifyStudentDetails(StudentAttributes[] students) {
+ verifyTableBodyValues(getStudentList(), getExpectedStudentValues(students));
+ }
+
+ public void verifyNumStudents(int expected) {
+ assertEquals(expected, getNumStudents());
+ }
+
+ public void sendInvite(StudentAttributes student) {
+ clickAndConfirm(getSendInviteButton(student));
+ }
+
+ public void remindAllToJoin() {
+ clickAndConfirm(waitForElementPresence(By.id("btn-remind-all")));
+ }
+
+ public void downloadStudentList() {
+ click(waitForElementPresence(By.id("btn-download")));
+ }
+
+ public void sortByName() {
+ click(browser.driver.findElement(By.id("sort-by-name")));
+ waitUntilAnimationFinish();
+ }
+
+ public void sortByStatus() {
+ click(browser.driver.findElement(By.id("sort-by-status")));
+ waitUntilAnimationFinish();
+ }
+
+ public void deleteStudent(StudentAttributes student) {
+ clickAndConfirm(getDeleteButton(student));
+ }
+
+ public void deleteAllStudents() {
+ clickAndConfirm(waitForElementPresence(By.id("btn-delete-all")));
+ }
+
+ private String getExpectedInstructorString(InstructorAttributes[] instructors) {
+ StringJoiner expected = new StringJoiner(System.lineSeparator());
+ for (InstructorAttributes instructor : instructors) {
+ expected.add(instructor.getRole() + ": " + instructor.getName() + " (" + instructor.getEmail() + ")");
+ }
+ return expected.toString();
+ }
+
+ private WebElement getStudentList() {
+ return browser.driver.findElement(By.cssSelector("#student-list table"));
+ }
+
+ private String[][] getExpectedStudentValues(StudentAttributes[] students) {
+ String[][] expected = new String[students.length][6];
+ for (int i = 0; i < students.length; i++) {
+ StudentAttributes student = students[i];
+ expected[i][0] = "View Photo";
+ expected[i][1] = student.getSection();
+ expected[i][2] = student.getTeam();
+ expected[i][3] = student.getName();
+ expected[i][4] = student.getGoogleId().isEmpty() ? "Yet to Join" : "Joined";
+ expected[i][5] = student.getEmail();
+ }
+ return expected;
+ }
+
+ private WebElement getSendInviteButton(StudentAttributes student) {
+ WebElement studentRow = getStudentRow(student);
+ return studentRow.findElement(By.id("btn-send-invite"));
+ }
+
+ private WebElement getDeleteButton(StudentAttributes student) {
+ WebElement studentRow = getStudentRow(student);
+ return studentRow.findElement(By.id("btn-delete"));
+ }
+
+ private List getAllStudentRows() {
+ return getStudentList().findElements(By.cssSelector("tbody tr"));
+ }
+
+ private int getNumStudents() {
+ try {
+ return getAllStudentRows().size();
+ } catch (NoSuchElementException e) {
+ return 0;
+ }
+ }
+
+ private WebElement getStudentRow(StudentAttributes student) {
+ List studentRows = getAllStudentRows();
+ for (WebElement studentRow : studentRows) {
+ List studentCells = studentRow.findElements(By.tagName("td"));
+ if (studentCells.get(5).getText().equals(student.getEmail())) {
+ return studentRow;
+ }
+ }
+ return null;
+ }
+
+ public InstructorCourseStudentDetailsViewPage clickViewStudent(StudentAttributes student) {
+ WebElement studentRow = getStudentRow(student);
+ WebElement viewButton = studentRow.findElement(By.id("btn-view-details"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorCourseStudentDetailsViewPage.class);
+ }
+
+ public InstructorCourseStudentDetailsEditPage clickEditStudent(StudentAttributes student) {
+ WebElement studentRow = getStudentRow(student);
+ WebElement viewButton = studentRow.findElement(By.id("btn-edit-details"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorCourseStudentDetailsEditPage.class);
+ }
+
+ public InstructorStudentRecordsPage clickViewAllRecords(StudentAttributes student) {
+ WebElement studentRow = getStudentRow(student);
+ WebElement viewButton = studentRow.findElement(By.id("btn-view-records"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorStudentRecordsPage.class);
+ }
+
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseEditPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseEditPage.java
index 53f0c2c38a4..e4621310ed5 100644
--- a/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseEditPage.java
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseEditPage.java
@@ -16,6 +16,7 @@
import teammates.common.datatransfer.attributes.CourseAttributes;
import teammates.common.datatransfer.attributes.InstructorAttributes;
import teammates.common.util.Const;
+import teammates.common.util.ThreadHelper;
/**
* Represents the instructor course edit page of the website.
@@ -94,7 +95,7 @@ public void verifyInstructorDetails(InstructorAttributes instructor) {
} else {
assertEquals("(This instructor will NOT be displayed to students)", getInstructorDisplayName(instrNum));
}
- assertEquals(getRoleIndex(instructor.role), getInstructorAccessLevel(instrNum));
+ assertEquals(instructor.role, getInstructorRole(instrNum));
if (instructor.role.equals(Const.InstructorPermissionRoleNames.INSTRUCTOR_PERMISSION_ROLE_CUSTOM)
&& getEditInstructorButton(instrNum).isEnabled()) {
verifyCustomPrivileges(instrNum, instructor.privileges);
@@ -200,10 +201,10 @@ public void addInstructor(InstructorAttributes newInstructor) {
fillTextBox(getNameField(instructorIndex), newInstructor.name);
fillTextBox(getEmailField(instructorIndex), newInstructor.email);
if (newInstructor.isDisplayedToStudents) {
- markCheckBoxAsChecked(getDisplayedToStudentCheckBox(instructorIndex));
+ markOptionAsSelected(getDisplayedToStudentCheckBox(instructorIndex));
fillTextBox(getDisplayNameField(instructorIndex), newInstructor.displayedName);
} else {
- markCheckBoxAsUnchecked(getDisplayedToStudentCheckBox(instructorIndex));
+ markOptionAsUnselected(getDisplayedToStudentCheckBox(instructorIndex));
}
selectRoleForInstructor(instructorIndex, getRoleIndex(newInstructor.role));
clickSaveInstructorButton(instructorIndex);
@@ -225,17 +226,17 @@ public void editInstructor(int instrNum, InstructorAttributes instructor) {
fillTextBox(getNameField(instrNum), instructor.name);
fillTextBox(getEmailField(instrNum), instructor.email);
if (instructor.isDisplayedToStudents) {
- markCheckBoxAsChecked(getDisplayedToStudentCheckBox(instrNum));
+ markOptionAsSelected(getDisplayedToStudentCheckBox(instrNum));
fillTextBox(getDisplayNameField(instrNum), instructor.displayedName);
} else {
- markCheckBoxAsUnchecked(getDisplayedToStudentCheckBox(instrNum));
+ markOptionAsUnselected(getDisplayedToStudentCheckBox(instrNum));
}
selectRoleForInstructor(instrNum, getRoleIndex(instructor.role));
clickSaveInstructorButton(instrNum);
}
public void toggleCustomCourseLevelPrivilege(int instrNum, String privilege) {
- if (getInstructorAccessLevel(instrNum) != INSTRUCTOR_TYPE_CUSTOM) {
+ if (!getInstructorRole(instrNum).equals(Const.InstructorPermissionRoleNames.INSTRUCTOR_PERMISSION_ROLE_CUSTOM)) {
return;
}
@@ -246,7 +247,7 @@ public void toggleCustomCourseLevelPrivilege(int instrNum, String privilege) {
public void toggleCustomSectionLevelPrivilege(int instrNum, int panelNum, String section,
String privilege) {
- if (getInstructorAccessLevel(instrNum) != INSTRUCTOR_TYPE_CUSTOM) {
+ if (!getInstructorRole(instrNum).equals(Const.InstructorPermissionRoleNames.INSTRUCTOR_PERMISSION_ROLE_CUSTOM)) {
return;
}
@@ -260,7 +261,7 @@ public void toggleCustomSectionLevelPrivilege(int instrNum, int panelNum, String
public void toggleCustomSessionLevelPrivilege(int instrNum, int panelNum, String section, String session,
String privilege) {
- if (getInstructorAccessLevel(instrNum) != INSTRUCTOR_TYPE_CUSTOM) {
+ if (!getInstructorRole(instrNum).equals(Const.InstructorPermissionRoleNames.INSTRUCTOR_PERMISSION_ROLE_CUSTOM)) {
return;
}
@@ -307,7 +308,7 @@ private void clickCancelInstructorButton(int instrNum) {
private void clickSaveInstructorButton(int instrNum) {
click(getSaveInstructorButton(instrNum));
- waitForPageToLoad(true);
+ ThreadHelper.waitFor(1000);
}
private void clickAddSectionPrivilegeLink(int instrNum) {
@@ -388,15 +389,9 @@ public String getInstructorDisplayName(int instrNum) {
return browser.driver.findElement(By.id("displayed-name-instructor-" + instrNum)).getAttribute("value");
}
- public int getInstructorAccessLevel(int instrNum) {
- List accessLevelCheckboxes = browser.driver.findElements(
- By.cssSelector("#access-levels-instructor-" + instrNum + " input"));
- for (int i = 0; i < accessLevelCheckboxes.size(); i++) {
- if (accessLevelCheckboxes.get(i).isSelected()) {
- return i;
- }
- }
- return -1;
+ public String getInstructorRole(int instrNum) {
+ String roleAndDescription = browser.driver.findElement(By.id("role-instructor-" + instrNum)).getText();
+ return roleAndDescription.split(":")[0];
}
private WebElement getAccessLevels(int instrNum) {
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseEnrollPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseEnrollPage.java
index 5f8fd3d9040..8f0bd6211a4 100644
--- a/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseEnrollPage.java
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseEnrollPage.java
@@ -19,6 +19,9 @@ public class InstructorCourseEnrollPage extends AppPage {
private static final int SPREADSHEET_NUM_STARTING_ROWS = 20;
private static final int NUM_ENROLLMENT_ATTRIBUTES = 5;
+ @FindBy(id = "enroll-header")
+ private WebElement enrollHeader;
+
@FindBy(id = "toggle-existing-students")
private WebElement toggleExistingStudentsHeader;
@@ -49,8 +52,13 @@ protected boolean containsExpectedPageContents() {
return getPageTitle().contains("Enroll Students for");
}
+ public void verifyIsCorrectPage(String courseId) {
+ assertEquals("Enroll Students for " + courseId, enrollHeader.getText());
+ }
+
public void clickToggleExistingStudentsHeader() {
click(toggleExistingStudentsHeader);
+ waitUntilAnimationFinish();
}
public void clickEnrollButton() {
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseStudentDetailsEditPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseStudentDetailsEditPage.java
new file mode 100644
index 00000000000..a34c01f030c
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseStudentDetailsEditPage.java
@@ -0,0 +1,81 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.attributes.StudentAttributes;
+
+/**
+ * Represents the instructor course student details edit page of the website.
+ */
+public class InstructorCourseStudentDetailsEditPage extends AppPage {
+
+ @FindBy (id = "courseid")
+ private WebElement courseId;
+
+ @FindBy (id = "studentname")
+ private WebElement studentNameTextbox;
+
+ @FindBy (id = "sectionname")
+ private WebElement sectionNameTextbox;
+
+ @FindBy (id = "teamname")
+ private WebElement teamNameTextbox;
+
+ @FindBy (id = "newstudentemail")
+ private WebElement studentEmailTextbox;
+
+ @FindBy (id = "comments")
+ private WebElement commentsTextbox;
+
+ @FindBy (id = "btn-submit")
+ private WebElement submitButton;
+
+ public InstructorCourseStudentDetailsEditPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ return getPageTitle().contains("Edit Student Details");
+ }
+
+ public void verifyIsCorrectPage(String expectedCourseId, String expectedStudentEmail) {
+ assertEquals(expectedCourseId, courseId.getText());
+ assertEquals(expectedStudentEmail, studentEmailTextbox.getAttribute("value"));
+ }
+
+ public void verifyStudentDetails(StudentAttributes studentDetails) {
+ assertEquals(studentDetails.getCourse(), courseId.getText());
+ assertEquals(studentDetails.getName(), studentNameTextbox.getAttribute("value"));
+ if (studentDetails.getSection() == null) {
+ assertEquals("None", sectionNameTextbox.getAttribute("value"));
+ } else {
+ assertEquals(studentDetails.getSection(), sectionNameTextbox.getAttribute("value"));
+ }
+ assertEquals(studentDetails.getTeam(), teamNameTextbox.getAttribute("value"));
+ assertEquals(studentDetails.getEmail(), studentEmailTextbox.getAttribute("value"));
+ if (studentDetails.getComments() != null) {
+ assertEquals(studentDetails.getComments(), commentsTextbox.getAttribute("value"));
+ }
+ }
+
+ public void editStudentDetails(StudentAttributes newStudentDetails) {
+ fillTextBox(studentNameTextbox, newStudentDetails.getName());
+ fillTextBox(sectionNameTextbox, newStudentDetails.getSection());
+ fillTextBox(teamNameTextbox, newStudentDetails.getTeam());
+ if (newStudentDetails.getComments() != null) {
+ fillTextBox(commentsTextbox, newStudentDetails.getComments());
+ }
+ clickAndConfirm(submitButton);
+ }
+
+ public void editStudentEmailAndResendLinks(String newEmail) {
+ fillTextBox(studentEmailTextbox, newEmail);
+ click(submitButton);
+ click(waitForElementPresence(By.id("btn-resend-links")));
+ }
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseStudentDetailsViewPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseStudentDetailsViewPage.java
new file mode 100644
index 00000000000..ce283ddefc2
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorCourseStudentDetailsViewPage.java
@@ -0,0 +1,105 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.attributes.StudentAttributes;
+import teammates.common.datatransfer.attributes.StudentProfileAttributes;
+
+/**
+ * Represents the instructor course student details view page of the website.
+ */
+public class InstructorCourseStudentDetailsViewPage extends AppPage {
+ private static final String NOT_SPECIFIED_LABEL = "Not Specified";
+
+ @FindBy (id = "student-name")
+ private WebElement studentName;
+
+ @FindBy (id = "name-with-gender")
+ private WebElement studentNameWithGender;
+
+ @FindBy (id = "personal-email")
+ private WebElement studentPersonalEmail;
+
+ @FindBy (id = "institution")
+ private WebElement studentInstitution;
+
+ @FindBy (id = "nationality")
+ private WebElement studentNationality;
+
+ @FindBy (id = "course-id")
+ private WebElement courseId;
+
+ @FindBy (id = "section-name")
+ private WebElement studentSectionName;
+
+ @FindBy (id = "team-name")
+ private WebElement studentTeamName;
+
+ @FindBy (id = "email")
+ private WebElement studentOfficialEmail;
+
+ @FindBy (id = "comments")
+ private WebElement studentComments;
+
+ @FindBy (id = "more-info")
+ private WebElement moreInformation;
+
+ public InstructorCourseStudentDetailsViewPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ return getPageSource().contains("Enrollment Details");
+ }
+
+ public void verifyIsCorrectPage(String expectedCourseId, String expectedStudentEmail) {
+ verifyDetail(expectedCourseId, courseId);
+ verifyDetail(expectedStudentEmail, studentOfficialEmail);
+ }
+
+ public void verifyStudentDetails(StudentProfileAttributes studentProfile, StudentAttributes student) {
+ verifyDetail(student.getName(), studentName);
+
+ StudentProfileAttributes profileToTest = studentProfile;
+ if (studentProfile == null) {
+ profileToTest = StudentProfileAttributes.builder(student.getGoogleId()).build();
+ }
+ verifyDetail(getExpectedNameWithGender(profileToTest), studentNameWithGender);
+ verifyDetail(profileToTest.getEmail(), studentPersonalEmail);
+ verifyDetail(profileToTest.getInstitute(), studentInstitution);
+ verifyDetail(profileToTest.getNationality(), studentNationality);
+
+ verifyDetail(student.getCourse(), courseId);
+ verifyDetail(student.getSection(), studentSectionName);
+ verifyDetail(student.getTeam(), studentTeamName);
+ verifyDetail(student.getEmail(), studentOfficialEmail);
+ verifyDetail(student.getComments(), studentComments);
+
+ verifyDetail(profileToTest.getMoreInfo(), moreInformation);
+ }
+
+ private void verifyDetail(String expected, WebElement detailField) {
+ if (expected.isEmpty()) {
+ assertEquals(NOT_SPECIFIED_LABEL, detailField.getText());
+ } else {
+ assertEquals(expected, detailField.getText());
+ }
+ }
+
+ private String getExpectedNameWithGender(StudentProfileAttributes profile) {
+ String name = profile.getShortName();
+ StudentProfileAttributes.Gender gender = profile.getGender();
+ String expectedName = name.isEmpty()
+ ? NOT_SPECIFIED_LABEL
+ : name;
+ String expectedGender = gender.equals(StudentProfileAttributes.Gender.OTHER)
+ ? NOT_SPECIFIED_LABEL
+ : gender.toString();
+
+ return expectedName + " (" + expectedGender + ")";
+ }
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorCoursesPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorCoursesPage.java
index 07f6d5b98de..2eb795198c1 100644
--- a/src/e2e/java/teammates/e2e/pageobjects/InstructorCoursesPage.java
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorCoursesPage.java
@@ -15,6 +15,7 @@
import org.openqa.selenium.support.ui.Select;
import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.util.TimeHelper;
/**
* Represents the "Courses" page for Instructors.
@@ -78,13 +79,15 @@ public void verifyActiveCoursesDetails(CourseAttributes[] courses) {
public void verifyActiveCourseStatistics(CourseAttributes course, String numSections, String numTeams,
String numStudents, String numUnregistered) {
showStatistics(course.getId());
- String[] courseDetail = { course.getId(), course.getName(), course.getCreatedAtDateString(),
+ String[] courseDetail = { course.getId(), course.getName(),
+ TimeHelper.formatInstant(course.getCreatedAt(), course.getTimeZone(), "d MMM yyyy"),
numSections, numTeams, numStudents, numUnregistered };
verifyTableRowValues(getActiveTableRow(course.getId()), courseDetail);
}
public void verifyArchivedCoursesDetails(CourseAttributes[] courses) {
showArchiveTable();
+ this.waitUntilAnimationFinish();
String[][] courseDetails = getCourseDetails(courses);
for (int i = 0; i < courses.length; i++) {
// use verifyTableRowValues as archive courses are not sorted
@@ -94,6 +97,7 @@ public void verifyArchivedCoursesDetails(CourseAttributes[] courses) {
public void verifyDeletedCoursesDetails(CourseAttributes[] courses) {
showDeleteTable();
+ this.waitUntilAnimationFinish();
String[][] courseDetails = getDeletedCourseDetails(courses);
for (int i = 0; i < courses.length; i++) {
// use verifyTableRowValues as deleted courses are not sorted
@@ -150,7 +154,7 @@ public void archiveCourse(String courseId) {
click(otherActionButton);
click(getArchiveButton(courseId));
- waitForElementStaleness(otherActionButton);
+ waitUntilAnimationFinish();
}
public void moveCourseToRecycleBin(String courseId) {
@@ -158,21 +162,21 @@ public void moveCourseToRecycleBin(String courseId) {
click(otherActionButton);
clickAndConfirm(getMoveToRecycleBinButton(courseId));
- waitForElementStaleness(otherActionButton);
+ waitUntilAnimationFinish();
}
public void unarchiveCourse(String courseId) {
WebElement unarchiveButton = getUnarchiveButton(courseId);
click(unarchiveButton);
- waitForElementStaleness(unarchiveButton);
+ waitUntilAnimationFinish();
}
public void moveArchivedCourseToRecycleBin(String courseId) {
WebElement moveArchivedToRecycleBinButton = getMoveArchivedToRecycleBinButton(courseId);
clickAndConfirm(moveArchivedToRecycleBinButton);
- waitForElementStaleness(moveArchivedToRecycleBinButton);
+ waitUntilAnimationFinish();
}
public void showDeleteTable() {
@@ -191,28 +195,28 @@ public void restoreCourse(String courseId) {
WebElement restoreButton = getRestoreButton(courseId);
click(restoreButton);
- waitForElementStaleness(restoreButton);
+ waitUntilAnimationFinish();
}
public void deleteCourse(String courseId) {
WebElement deleteButton = getDeleteButton(courseId);
clickAndConfirm(deleteButton);
- waitForElementStaleness(deleteButton);
+ waitUntilAnimationFinish();
}
public void restoreAllCourses() {
WebElement restoreAllButton = getRestoreAllButton();
click(restoreAllButton);
- waitForElementStaleness(restoreAllButton);
+ waitUntilAnimationFinish();
}
public void deleteAllCourses() {
WebElement deleteAllButton = getDeleteAllButton();
clickAndConfirm(deleteAllButton);
- waitForElementStaleness(deleteAllButton);
+ waitUntilAnimationFinish();
}
public void sortByCourseName() {
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java
new file mode 100644
index 00000000000..a786e4c8d3a
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java
@@ -0,0 +1,1582 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.FeedbackParticipantType;
+import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes;
+import teammates.common.datatransfer.attributes.FeedbackSessionAttributes;
+import teammates.common.datatransfer.attributes.InstructorAttributes;
+import teammates.common.datatransfer.attributes.StudentAttributes;
+import teammates.common.datatransfer.questions.FeedbackConstantSumQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackContributionQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackMcqQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackMsqQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackNumericalScaleQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackQuestionType;
+import teammates.common.datatransfer.questions.FeedbackRankOptionsQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackRankQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackRubricQuestionDetails;
+import teammates.common.datatransfer.questions.FeedbackTextQuestionDetails;
+import teammates.common.util.Const;
+import teammates.common.util.ThreadHelper;
+
+/**
+ * Represents the instructor feedback edit page of the website.
+ */
+public class InstructorFeedbackEditPage extends AppPage {
+ private static final String CUSTOM_FEEDBACK_PATH_OPTION = "Custom feedback path";
+ private static final String FEEDBACK_PATH_SEPARATOR = " will give feedback on ";
+ private static final String CUSTOM_VISIBILITY_OPTION = "Custom visibility options";
+
+ @FindBy(id = "btn-fs-edit")
+ private WebElement fsEditButton;
+
+ @FindBy(id = "btn-fs-save")
+ private WebElement fsSaveButton;
+
+ @FindBy(id = "btn-fs-delete")
+ private WebElement fsDeleteButton;
+
+ @FindBy(id = "btn-fs-copy")
+ private WebElement fsCopyButton;
+
+ @FindBy(id = "edit-course-id")
+ private WebElement courseIdTextBox;
+
+ @FindBy(id = "time-zone")
+ private WebElement timezoneDropDown;
+
+ @FindBy(id = "course-name")
+ private WebElement courseNameTextBox;
+
+ @FindBy(id = "edit-session-name")
+ private WebElement sessionNameTextBox;
+
+ @FindBy(id = "instructions")
+ private WebElement instructionsEditor;
+
+ @FindBy(id = "submission-start-date")
+ private WebElement startDateBox;
+
+ @FindBy(id = "submission-start-time")
+ private WebElement startTimeDropdown;
+
+ @FindBy(id = "submission-end-date")
+ private WebElement endDateBox;
+
+ @FindBy(id = "submission-end-time")
+ private WebElement endTimeDropdown;
+
+ @FindBy(id = "grace-period")
+ private WebElement gracePeriodDropdown;
+
+ @FindBy(id = "submission-status")
+ private WebElement submissionStatusTextBox;
+
+ @FindBy(id = "published-status")
+ private WebElement publishStatusTextBox;
+
+ @FindBy(id = "btn-change-visibility")
+ private WebElement changeVisibilityButton;
+
+ @FindBy(id = "session-visibility-custom")
+ private WebElement customSessionVisibleTimeButton;
+
+ @FindBy(id = "session-visibility-date")
+ private WebElement sessionVisibilityDateBox;
+
+ @FindBy(id = "session-visibility-time")
+ private WebElement sessionVisibilityTimeDropdown;
+
+ @FindBy(id = "session-visibility-at-open")
+ private WebElement openSessionVisibleTimeButton;
+
+ @FindBy(id = "response-visibility-custom")
+ private WebElement customResponseVisibleTimeButton;
+
+ @FindBy(id = "response-visibility-date")
+ private WebElement responseVisibilityDateBox;
+
+ @FindBy(id = "response-visibility-time")
+ private WebElement responseVisibilityTimeDropdown;
+
+ @FindBy(id = "response-visibility-immediately")
+ private WebElement immediateResponseVisibleTimeButton;
+
+ @FindBy(id = "response-visibility-manually")
+ private WebElement manualResponseVisibleTimeButton;
+
+ @FindBy(id = "btn-change-email")
+ private WebElement changeEmailButton;
+
+ @FindBy(id = "email-opening")
+ private WebElement openingSessionEmailCheckbox;
+
+ @FindBy(id = "email-closing")
+ private WebElement closingSessionEmailCheckbox;
+
+ @FindBy(id = "email-published")
+ private WebElement publishedSessionEmailCheckbox;
+
+ @FindBy(id = "btn-new-question")
+ private WebElement addNewQuestionButton;
+
+ @FindBy(id = "btn-copy-question")
+ private WebElement copyQuestionButton;
+
+ @FindBy(id = "preview-student")
+ private WebElement previewAsStudentDropdown;
+
+ @FindBy(id = "btn-preview-student")
+ private WebElement previewAsStudentButton;
+
+ @FindBy(id = "preview-instructor")
+ private WebElement previewAsInstructorDropdown;
+
+ @FindBy(id = "btn-preview-instructor")
+ private WebElement previewAsInstructorButton;
+
+ public InstructorFeedbackEditPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ return getPageTitle().contains("Edit Feedback Session");
+ }
+
+ public void verifySessionDetails(CourseAttributes course, FeedbackSessionAttributes feedbackSession) {
+ waitForElementPresence(By.id("instructions"));
+ assertEquals(getCourseId(), course.getId());
+ assertEquals(getCourseName(), course.getName());
+ assertEquals(getTimeZone(), feedbackSession.getTimeZone().toString());
+ assertEquals(getFeedbackSessionName(), feedbackSession.getFeedbackSessionName());
+ assertEquals(getInstructions(), feedbackSession.getInstructions());
+ assertEquals(getStartDate(), getDateString(feedbackSession.getStartTime(), feedbackSession.getTimeZone()));
+ assertEquals(getStartTime(), getTimeString(feedbackSession.getStartTime(), feedbackSession.getTimeZone()));
+ assertEquals(getEndDate(), getDateString(feedbackSession.getEndTime(), feedbackSession.getTimeZone()));
+ assertEquals(getEndTime(), getTimeString(feedbackSession.getEndTime(), feedbackSession.getTimeZone()));
+ assertEquals(getGracePeriod(), feedbackSession.getGracePeriodMinutes() + " min");
+ verifySubmissionStatus(feedbackSession);
+ verifyPublishedStatus(feedbackSession);
+ verifyVisibilitySettings(feedbackSession);
+ verifyEmailSettings(feedbackSession);
+ }
+
+ private void verifySubmissionStatus(FeedbackSessionAttributes feedbackSession) {
+ String submissionStatus = getSubmissionStatus();
+ if (feedbackSession.isClosed()) {
+ assertEquals(submissionStatus, "Closed");
+ } else if (feedbackSession.isVisible() && (feedbackSession.isOpened() || feedbackSession.isInGracePeriod())) {
+ assertEquals(submissionStatus, "Open");
+ } else {
+ assertEquals(submissionStatus, "Awaiting");
+ }
+ }
+
+ private void verifyPublishedStatus(FeedbackSessionAttributes feedbackSession) {
+ String publishedStatus = getPublishedStatus();
+ if (feedbackSession.isPublished()) {
+ assertEquals(publishedStatus, "Published");
+ } else {
+ assertEquals(publishedStatus, "Not Published");
+ }
+ }
+
+ private void verifyVisibilitySettings(FeedbackSessionAttributes feedbackSession) {
+ Instant sessionVisibleTime = feedbackSession.getSessionVisibleFromTime();
+ Instant responseVisibleTime = feedbackSession.getResultsVisibleFromTime();
+
+ // Default settings, assert setting section not expanded
+ if (sessionVisibleTime.equals(Const.TIME_REPRESENTS_FOLLOW_OPENING)
+ && responseVisibleTime.equals(Const.TIME_REPRESENTS_LATER)) {
+ assertTrue(isElementPresent("btn-change-visibility"));
+ return;
+ }
+ verifySessionVisibilitySettings(sessionVisibleTime, feedbackSession);
+ verifyResponseVisibilitySettings(responseVisibleTime, feedbackSession);
+ }
+
+ private void verifySessionVisibilitySettings(Instant sessionVisibleTime,
+ FeedbackSessionAttributes feedbackSession) {
+ if (sessionVisibleTime.equals(Const.TIME_REPRESENTS_FOLLOW_OPENING)) {
+ assertTrue(openSessionVisibleTimeButton.isSelected());
+ } else {
+ assertTrue(customSessionVisibleTimeButton.isSelected());
+ assertEquals(getSessionVisibilityDate(), getDateString(feedbackSession.getSessionVisibleFromTime(),
+ feedbackSession.getTimeZone()));
+ assertEquals(getSessionVisibilityTime(), getTimeString(feedbackSession.getSessionVisibleFromTime(),
+ feedbackSession.getTimeZone()));
+ }
+ }
+
+ private void verifyResponseVisibilitySettings(Instant responseVisibleTime,
+ FeedbackSessionAttributes feedbackSession) {
+ if (responseVisibleTime.equals(Const.TIME_REPRESENTS_FOLLOW_VISIBLE)) {
+ assertTrue(immediateResponseVisibleTimeButton.isSelected());
+ } else if (responseVisibleTime.equals(Const.TIME_REPRESENTS_LATER)) {
+ assertTrue(manualResponseVisibleTimeButton.isSelected());
+ } else {
+ assertTrue(customSessionVisibleTimeButton.isSelected());
+ assertEquals(getResponseVisibilityDate(), getDateString(feedbackSession.getResultsVisibleFromTime(),
+ feedbackSession.getTimeZone()));
+ assertEquals(getResponseVisibilityTime(), getTimeString(feedbackSession.getResultsVisibleFromTime(),
+ feedbackSession.getTimeZone()));
+ }
+ }
+
+ private void verifyEmailSettings(FeedbackSessionAttributes feedbackSession) {
+ boolean isOpeningEmailEnabled = feedbackSession.isOpeningEmailEnabled();
+ boolean isClosingEmailEnabled = feedbackSession.isClosingEmailEnabled();
+ boolean isPublishedEmailEnabled = feedbackSession.isPublishedEmailEnabled();
+
+ // Default settings, assert setting section not expanded
+ if (isOpeningEmailEnabled && isClosingEmailEnabled && isPublishedEmailEnabled) {
+ assertTrue(isElementPresent("btn-change-email"));
+ return;
+ }
+ if (isOpeningEmailEnabled) {
+ assertTrue(openingSessionEmailCheckbox.isSelected());
+ }
+ if (isClosingEmailEnabled) {
+ assertTrue(closingSessionEmailCheckbox.isSelected());
+ }
+ if (isPublishedEmailEnabled) {
+ assertTrue(publishedSessionEmailCheckbox.isSelected());
+ }
+ }
+
+ public void editSessionDetails(FeedbackSessionAttributes newFeedbackSessionDetails) {
+ click(fsEditButton);
+ setInstructions(newFeedbackSessionDetails.getInstructions());
+ setSessionStartDateTime(newFeedbackSessionDetails.getStartTime(), newFeedbackSessionDetails.getTimeZone());
+ setSessionEndDateTime(newFeedbackSessionDetails.getEndTime(), newFeedbackSessionDetails.getTimeZone());
+ selectGracePeriod(newFeedbackSessionDetails.getGracePeriodMinutes());
+ setVisibilitySettings(newFeedbackSessionDetails);
+ setEmailSettings(newFeedbackSessionDetails);
+ click(fsSaveButton);
+ }
+
+ public void copySessionToOtherCourse(CourseAttributes otherCourse, String sessionName) {
+ click(fsCopyButton);
+ WebElement copyFsModal = waitForElementPresence(By.id("copy-course-modal"));
+
+ fillTextBox(copyFsModal.findElement(By.id("copy-session-name")), sessionName);
+ List options = copyFsModal.findElements(By.className("form-check"));
+ for (WebElement option : options) {
+ String courseId = option.findElement(By.cssSelector("label span")).getText();
+ if (courseId.equals(otherCourse.getId())) {
+ click(option.findElement(By.tagName("input")));
+ break;
+ }
+ }
+ click(browser.driver.findElement(By.id("btn-confirm-copy-course")));
+ }
+
+ public void deleteSession() {
+ clickAndConfirm(fsDeleteButton);
+ }
+
+ public FeedbackSubmitPage previewAsStudent(StudentAttributes student) {
+ selectDropdownOptionByText(previewAsStudentDropdown, String.format("[%s] %s", student.team, student.name));
+ click(previewAsStudentButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(FeedbackSubmitPage.class);
+ }
+
+ public FeedbackSubmitPage previewAsInstructor(InstructorAttributes instructor) {
+ selectDropdownOptionByText(previewAsInstructorDropdown, instructor.name);
+ click(previewAsInstructorButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(FeedbackSubmitPage.class);
+ }
+
+ public void verifyNumQuestions(int expected) {
+ assertEquals(getNumQuestions(), expected);
+ }
+
+ public void verifyQuestionDetails(int questionNum, FeedbackQuestionAttributes feedbackQuestion) {
+ scrollElementToCenter(getQuestionForm(questionNum));
+ assertEquals(feedbackQuestion.getQuestionType(), getQuestionType(questionNum));
+ assertEquals(feedbackQuestion.getQuestionNumber(), getQuestionNumber(questionNum));
+ assertEquals(feedbackQuestion.getQuestionDetails().getQuestionText(), getQuestionBrief(questionNum));
+ assertEquals(getQuestionDescription(questionNum), feedbackQuestion.getQuestionDescription());
+ verifyFeedbackPathSettings(questionNum, feedbackQuestion);
+ verifyQuestionVisibilitySettings(questionNum, feedbackQuestion);
+ }
+
+ private void verifyFeedbackPathSettings(int questionNum, FeedbackQuestionAttributes feedbackQuestion) {
+ assertEquals(feedbackQuestion.getGiverType().toDisplayGiverName(), getFeedbackGiver(questionNum));
+ String feedbackReceiver = getFeedbackReceiver(questionNum);
+ assertEquals(feedbackQuestion.getRecipientType().toDisplayRecipientName(), feedbackReceiver);
+
+ if (feedbackReceiver.equals(FeedbackParticipantType.INSTRUCTORS.toDisplayRecipientName())
+ || feedbackReceiver.equals(FeedbackParticipantType.STUDENTS_EXCLUDING_SELF.toDisplayRecipientName())
+ || feedbackReceiver.equals(FeedbackParticipantType.TEAMS_EXCLUDING_SELF.toDisplayRecipientName())) {
+ verifyNumberOfEntitiesToGiveFeedbackTo(questionNum, feedbackQuestion.getNumberOfEntitiesToGiveFeedbackTo());
+ }
+ }
+
+ private void verifyNumberOfEntitiesToGiveFeedbackTo(int questionNum, int numberOfEntitiesToGiveFeedbackTo) {
+ WebElement questionForm = getQuestionForm(questionNum);
+ if (numberOfEntitiesToGiveFeedbackTo == Const.MAX_POSSIBLE_RECIPIENTS) {
+ assertTrue(questionForm.findElement(By.id("unlimited-recipients")).isSelected());
+ } else {
+ assertTrue(questionForm.findElement(By.id("custom-recipients")).isSelected());
+ assertEquals(questionForm.findElement(By.id("custom-recipients-number")).getAttribute("value"),
+ Integer.toString(numberOfEntitiesToGiveFeedbackTo));
+ }
+ }
+
+ private void verifyQuestionVisibilitySettings(int questionNum, FeedbackQuestionAttributes feedbackQuestion) {
+ WebElement questionForm = getQuestionForm(questionNum);
+ String visibility = questionForm.findElement(By.cssSelector("#btn-question-visibility span")).getText();
+ List showResponsesTo = feedbackQuestion.getShowResponsesTo();
+ List showGiverNameTo = feedbackQuestion.getShowGiverNameTo();
+ List showRecipientNameTo = feedbackQuestion.getShowRecipientNameTo();
+
+ switch (visibility) {
+ case "Shown anonymously to recipient and giver's team members, visible to instructors":
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.RECEIVER));
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.OWN_TEAM_MEMBERS));
+ assertEquals(showResponsesTo.size(), 3);
+
+ assertTrue(showGiverNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertEquals(showGiverNameTo.size(), 1);
+
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showRecipientNameTo.size(), 2);
+ break;
+
+ case "Visible to instructors only":
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertEquals(showResponsesTo.size(), 1);
+
+ assertTrue(showGiverNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertEquals(showGiverNameTo.size(), 1);
+
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertEquals(showRecipientNameTo.size(), 1);
+ break;
+
+ case "Shown anonymously to recipient and instructors":
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showResponsesTo.size(), 2);
+
+ assertEquals(showGiverNameTo.size(), 0);
+
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showRecipientNameTo.size(), 2);
+ break;
+
+ case "Shown anonymously to recipient, visible to instructors":
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showResponsesTo.size(), 2);
+
+ assertTrue(showGiverNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertEquals(showGiverNameTo.size(), 1);
+
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showRecipientNameTo.size(), 2);
+ break;
+
+ case "Shown anonymously to recipient and giver/recipient's team members, visible to instructors":
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.RECEIVER));
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.OWN_TEAM_MEMBERS));
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.RECEIVER_TEAM_MEMBERS));
+ assertEquals(showResponsesTo.size(), 4);
+
+ assertTrue(showGiverNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertEquals(showGiverNameTo.size(), 1);
+
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showRecipientNameTo.size(), 2);
+ break;
+
+ case "Visible to recipient and instructors":
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showResponsesTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showResponsesTo.size(), 2);
+
+ assertTrue(showGiverNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showGiverNameTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showGiverNameTo.size(), 2);
+
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.INSTRUCTORS));
+ assertTrue(showRecipientNameTo.contains(FeedbackParticipantType.RECEIVER));
+ assertEquals(showRecipientNameTo.size(), 2);
+ break;
+
+ default:
+ verifyCustomQuestionVisibility(questionNum, feedbackQuestion);
+ break;
+ }
+ }
+
+ private void verifyCustomQuestionVisibility(int questionNum, FeedbackQuestionAttributes feedbackQuestion) {
+ WebElement questionForm = getQuestionForm(questionNum);
+ String visibility = questionForm.findElement(By.cssSelector("#btn-question-visibility span")).getText();
+ assertEquals(visibility, CUSTOM_VISIBILITY_OPTION);
+
+ FeedbackParticipantType giver = feedbackQuestion.getGiverType();
+ FeedbackParticipantType receiver = feedbackQuestion.getRecipientType();
+ WebElement customVisibilityTable = questionForm.findElement(By.id("custom-visibility-table"));
+ assertVisibilityBoxesSelected(customVisibilityTable, giver, receiver, feedbackQuestion.getShowResponsesTo(), 1);
+ assertVisibilityBoxesSelected(customVisibilityTable, giver, receiver, feedbackQuestion.getShowGiverNameTo(), 2);
+ assertVisibilityBoxesSelected(customVisibilityTable, giver, receiver, feedbackQuestion.getShowRecipientNameTo(), 3);
+ }
+
+ private void assertVisibilityBoxesSelected(WebElement table, FeedbackParticipantType giver,
+ FeedbackParticipantType receiver, List participants,
+ int colNum) {
+ List possibleTypes = new ArrayList(Arrays.asList(FeedbackParticipantType.RECEIVER,
+ FeedbackParticipantType.OWN_TEAM_MEMBERS, FeedbackParticipantType.RECEIVER_TEAM_MEMBERS,
+ FeedbackParticipantType.STUDENTS, FeedbackParticipantType.INSTRUCTORS));
+ if (!giver.equals(FeedbackParticipantType.STUDENTS)) {
+ possibleTypes.remove(FeedbackParticipantType.OWN_TEAM_MEMBERS);
+ }
+ if (!receiver.equals(FeedbackParticipantType.STUDENTS)) {
+ possibleTypes.remove(FeedbackParticipantType.RECEIVER_TEAM_MEMBERS);
+ }
+ if (receiver.equals(FeedbackParticipantType.NONE)
+ || receiver.equals(FeedbackParticipantType.SELF)
+ || receiver.equals(FeedbackParticipantType.OWN_TEAM)) {
+ possibleTypes.remove(FeedbackParticipantType.RECEIVER);
+ possibleTypes.remove(FeedbackParticipantType.RECEIVER_TEAM_MEMBERS);
+ }
+
+ List rows = table.findElements(By.tagName("tr"));
+ int index = colNum - 1;
+ for (FeedbackParticipantType participant : participants) {
+ assertTrue(rows.get(possibleTypes.indexOf(participant)).findElements(By.tagName("input")).get(index)
+ .isSelected());
+ }
+ }
+
+ public void addTemplateQuestion(int optionNum) {
+ addNewQuestion(1);
+ WebElement templateQuestionModal = waitForElementPresence(By.id("template-question-modal"));
+
+ click(templateQuestionModal.findElements(By.tagName("input")).get(optionNum - 1));
+ clickAndWaitForNewQuestion(browser.driver.findElement(By.id("btn-confirm-template")));
+ }
+
+ public void copyQuestion(String courseId, String questionText) {
+ click(copyQuestionButton);
+ WebElement copyQuestionModal = waitForElementPresence(By.id("copy-question-modal"));
+
+ List rows = copyQuestionModal.findElements(By.cssSelector("tbody tr"));
+ for (WebElement row : rows) {
+ List cells = row.findElements(By.tagName("td"));
+ if (cells.get(1).getText().equals(courseId) && cells.get(4).getText().equals(questionText)) {
+ markOptionAsSelected(cells.get(0).findElement(By.tagName("input")));
+ }
+ }
+ clickAndWaitForNewQuestion(browser.driver.findElement(By.id("btn-confirm-copy-question")));
+ }
+
+ public void editQuestionNumber(int questionNum, int newQuestionNumber) {
+ clickEditQuestionButton(questionNum);
+ selectDropdownOptionByText(getQuestionForm(questionNum).findElement(By.id("question-number-dropdown")),
+ Integer.toString(newQuestionNumber));
+ clickSaveQuestionButton(questionNum);
+ }
+
+ public void editQuestionDetails(int questionNum, FeedbackQuestionAttributes feedbackQuestion) {
+ clickEditQuestionButton(questionNum);
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ clickSaveQuestionButton(questionNum);
+ }
+
+ private void inputQuestionDetails(int questionNum, FeedbackQuestionAttributes feedbackQuestion) {
+ setQuestionBrief(questionNum, feedbackQuestion.getQuestionDetails().getQuestionText());
+ setQuestionDescription(questionNum, feedbackQuestion.getQuestionDescription());
+ FeedbackQuestionType questionType = feedbackQuestion.getQuestionType();
+ if (!questionType.equals(FeedbackQuestionType.CONTRIB)) {
+ setFeedbackPath(questionNum, feedbackQuestion);
+ setQuestionVisibility(questionNum, feedbackQuestion);
+ }
+ }
+
+ public void duplicateQuestion(int questionNum) {
+ clickAndWaitForNewQuestion(getQuestionForm(questionNum).findElement(By.id("btn-duplicate-question")));
+ }
+
+ public void deleteQuestion(int questionNum) {
+ clickAndConfirm(getQuestionForm(questionNum).findElement(By.id("btn-delete-question")));
+ }
+
+ public void verifyTextQuestionDetails(int questionNum, FeedbackTextQuestionDetails questionDetails) {
+ String recommendLength = getRecommendedTextLengthField(questionNum).getAttribute("value");
+ assertEquals(recommendLength, questionDetails.getRecommendedLength().toString());
+ }
+
+ public void addTextQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(2);
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackTextQuestionDetails questionDetails = (FeedbackTextQuestionDetails) feedbackQuestion.getQuestionDetails();
+ fillTextBox(getRecommendedTextLengthField(questionNum), questionDetails.getRecommendedLength().toString());
+ clickSaveNewQuestionButton();
+ }
+
+ public void editTextQuestion(int questionNum, FeedbackTextQuestionDetails textQuestionDetails) {
+ clickEditQuestionButton(questionNum);
+ WebElement recommendedTextLengthField = getRecommendedTextLengthField(questionNum);
+ waitForElementToBeClickable(recommendedTextLengthField);
+ fillTextBox(recommendedTextLengthField, textQuestionDetails.getRecommendedLength().toString());
+ clickSaveQuestionButton(questionNum);
+ }
+
+ public void verifyMcqQuestionDetails(int questionNum, FeedbackMcqQuestionDetails questionDetails) {
+ if (verifyGeneratedOptions(questionNum, questionDetails.getGenerateOptionsFor())) {
+ return;
+ }
+ verifyOptions(questionNum, questionDetails.getMcqChoices());
+ verifyOptionWeights(questionNum, questionDetails.hasAssignedWeights(), questionDetails.getMcqWeights());
+ verifyOtherOption(questionNum, questionDetails.isOtherEnabled(), questionDetails.getMcqOtherWeight());
+ }
+
+ public void addMcqQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(3);
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackMcqQuestionDetails questionDetails = (FeedbackMcqQuestionDetails) feedbackQuestion.getQuestionDetails();
+ inputMcqDetails(questionNum, questionDetails);
+ clickSaveNewQuestionButton();
+ }
+
+ public void editMcqQuestion(int questionNum, FeedbackMcqQuestionDetails questionDetails) {
+ clickEditQuestionButton(questionNum);
+ inputMcqDetails(questionNum, questionDetails);
+ clickSaveQuestionButton(questionNum);
+ }
+
+ public void verifyMsqQuestionDetails(int questionNum, FeedbackMsqQuestionDetails questionDetails) {
+ verifyMaxOptions(questionNum, questionDetails.getMaxSelectableChoices());
+ verifyMinOptions(questionNum, questionDetails.getMinSelectableChoices());
+ if (verifyGeneratedOptions(questionNum, questionDetails.getGenerateOptionsFor())) {
+ return;
+ }
+ verifyOptions(questionNum, questionDetails.getMsqChoices());
+ verifyOptionWeights(questionNum, questionDetails.hasAssignedWeights(), questionDetails.getMsqWeights());
+ verifyOtherOption(questionNum, questionDetails.isOtherEnabled(), questionDetails.getMsqOtherWeight());
+ }
+
+ public void addMsqQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(4);
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackMsqQuestionDetails questionDetails = (FeedbackMsqQuestionDetails) feedbackQuestion.getQuestionDetails();
+ inputMsqDetails(questionNum, questionDetails);
+ clickSaveNewQuestionButton();
+ }
+
+ public void editMsqQuestion(int questionNum, FeedbackMsqQuestionDetails msqQuestionDetails) {
+ clickEditQuestionButton(questionNum);
+ inputMsqDetails(questionNum, msqQuestionDetails);
+ clickSaveQuestionButton(questionNum);
+ }
+
+ public void verifyNumScaleQuestionDetails(int questionNum, FeedbackNumericalScaleQuestionDetails questionDetails) {
+ assertEquals(getMinNumscaleInput(questionNum).getAttribute("value"),
+ Integer.toString(questionDetails.getMinScale()));
+ assertEquals(getNumScaleIncrementInput(questionNum).getAttribute("value"),
+ getDoubleString(questionDetails.getStep()));
+ assertEquals(getMaxNumscaleInput(questionNum).getAttribute("value"),
+ Integer.toString(questionDetails.getMaxScale()));
+ }
+
+ public void addNumScaleQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(5);
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackNumericalScaleQuestionDetails questionDetails =
+ (FeedbackNumericalScaleQuestionDetails) feedbackQuestion.getQuestionDetails();
+ inputNumScaleDetails(questionNum, questionDetails);
+ clickSaveNewQuestionButton();
+ }
+
+ public void editNumScaleQuestion(int questionNum, FeedbackNumericalScaleQuestionDetails questionDetails) {
+ clickEditQuestionButton(questionNum);
+ inputNumScaleDetails(questionNum, questionDetails);
+ clickSaveQuestionButton(questionNum);
+ }
+
+ public void verifyConstSumQuestionDetails(int questionNum, FeedbackConstantSumQuestionDetails questionDetails) {
+ if (!questionDetails.isDistributeToRecipients()) {
+ verifyOptions(questionNum, questionDetails.getConstSumOptions());
+ }
+
+ if (questionDetails.isPointsPerOption()) {
+ assertTrue(getConstSumPerOptionPointsRadioBtn(questionNum).isSelected());
+ assertEquals(getConstSumPerOptionPointsInput(questionNum).getAttribute("value"),
+ Integer.toString(questionDetails.getPoints()));
+ assertFalse(getConstSumTotalPointsRadioBtn(questionNum).isSelected());
+ } else {
+ assertTrue(getConstSumTotalPointsRadioBtn(questionNum).isSelected());
+ assertEquals(getConstSumTotalPointsInput(questionNum).getAttribute("value"),
+ Integer.toString(questionDetails.getPoints()));
+ assertFalse(getConstSumPerOptionPointsRadioBtn(questionNum).isSelected());
+ }
+
+ if (questionDetails.isForceUnevenDistribution()) {
+ String distributeFor = questionDetails.getDistributePointsFor();
+ assertTrue(getConstSumUnevenDistributionCheckbox(questionNum).isSelected());
+ assertEquals(getSelectedDropdownOptionText(getConstSumUnevenDistributionDropdown(questionNum)).trim(),
+ "All options".equals(distributeFor) ? "Every option" : distributeFor);
+ } else {
+ assertFalse(getConstSumUnevenDistributionCheckbox(questionNum).isSelected());
+ }
+ }
+
+ public void addConstSumOptionQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(6);
+ addConstSumQuestion(feedbackQuestion);
+ }
+
+ public void addConstSumRecipientQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(7);
+ addConstSumQuestion(feedbackQuestion);
+ }
+
+ public void addConstSumQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackConstantSumQuestionDetails questionDetails =
+ (FeedbackConstantSumQuestionDetails) feedbackQuestion.getQuestionDetails();
+ inputConstSumDetails(questionNum, questionDetails);
+ clickSaveNewQuestionButton();
+ }
+
+ public void editConstSumQuestion(int questionNum, FeedbackConstantSumQuestionDetails csQuestionDetails) {
+ clickEditQuestionButton(questionNum);
+ inputConstSumDetails(questionNum, csQuestionDetails);
+ clickSaveQuestionButton(questionNum);
+ }
+
+ public void verifyContributionQuestionDetails(int questionNum, FeedbackContributionQuestionDetails questionDetails) {
+ assertEquals(questionDetails.isNotSureAllowed(), getAllowNotSureContributionCheckbox(questionNum).isSelected());
+ }
+
+ public void addContributionQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(8);
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackContributionQuestionDetails questionDetails =
+ (FeedbackContributionQuestionDetails) feedbackQuestion.getQuestionDetails();
+ inputContributionDetails(questionNum, questionDetails);
+ clickSaveNewQuestionButton();
+ }
+
+ public void editContributionQuestion(int questionNum, FeedbackContributionQuestionDetails questionDetails) {
+ clickEditQuestionButton(questionNum);
+ inputContributionDetails(questionNum, questionDetails);
+ clickSaveQuestionButton(questionNum);
+ }
+
+ public void verifyRubricQuestionDetails(int questionNum, FeedbackRubricQuestionDetails questionDetails) {
+ int numChoices = questionDetails.getNumOfRubricChoices();
+ List choices = questionDetails.getRubricChoices();
+ for (int i = 0; i < numChoices; i++) {
+ assertEquals(choices.get(i), getRubricChoiceInputs(questionNum).get(i).getAttribute("value"));
+ }
+
+ int numSubQn = questionDetails.getNumOfRubricSubQuestions();
+ List subQuestions = questionDetails.getRubricSubQuestions();
+ List> descriptions = questionDetails.getRubricDescriptions();
+ for (int i = 0; i < numSubQn; i++) {
+ List textAreas = getRubricTextareas(questionNum, i + 2);
+ assertEquals(subQuestions.get(i), textAreas.get(0).getAttribute("value"));
+ for (int j = 0; j < numChoices; j++) {
+ assertEquals(descriptions.get(i).get(j), textAreas.get(j + 1).getAttribute("value"));
+ }
+ }
+
+ if (questionDetails.hasAssignedWeights()) {
+ assertTrue(getWeightCheckbox(questionNum).isSelected());
+ List> weights = questionDetails.getRubricWeights();
+ for (int i = 0; i < numSubQn; i++) {
+ List rubricWeights = getRubricWeights(questionNum, i + 2);
+ for (int j = 0; j < numChoices; j++) {
+ assertEquals(rubricWeights.get(j).getAttribute("value"),
+ getDoubleString(weights.get(i).get(j)));
+ }
+ }
+ } else {
+ assertFalse(getWeightCheckbox(questionNum).isSelected());
+ }
+ }
+
+ public void addRubricQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(9);
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackRubricQuestionDetails questionDetails =
+ (FeedbackRubricQuestionDetails) feedbackQuestion.getQuestionDetails();
+ inputRubricDetails(questionNum, questionDetails);
+ clickSaveNewQuestionButton();
+ }
+
+ public void editRubricQuestion(int questionNum, FeedbackRubricQuestionDetails questionDetails) {
+ clickEditQuestionButton(questionNum);
+ inputRubricDetails(questionNum, questionDetails);
+ clickSaveQuestionButton(questionNum);
+ }
+
+ public void verifyRankQuestionDetails(int questionNum, FeedbackRankQuestionDetails questionDetails) {
+ if (questionDetails instanceof FeedbackRankOptionsQuestionDetails) {
+ FeedbackRankOptionsQuestionDetails optionDetails = (FeedbackRankOptionsQuestionDetails) questionDetails;
+ verifyOptions(questionNum, optionDetails.getOptions());
+ }
+ assertEquals(getAllowDuplicateRankCheckbox(questionNum).isSelected(), questionDetails.areDuplicatesAllowed());
+ verifyMaxOptions(questionNum, questionDetails.getMaxOptionsToBeRanked());
+ verifyMinOptions(questionNum, questionDetails.getMinOptionsToBeRanked());
+ }
+
+ public void addRankOptionsQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(10);
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackRankOptionsQuestionDetails questionDetails =
+ (FeedbackRankOptionsQuestionDetails) feedbackQuestion.getQuestionDetails();
+ inputRankDetails(questionNum, questionDetails);
+ clickSaveNewQuestionButton();
+ }
+
+ public void addRankRecipientsQuestion(FeedbackQuestionAttributes feedbackQuestion) {
+ addNewQuestion(11);
+ int questionNum = getNumQuestions();
+ inputQuestionDetails(questionNum, feedbackQuestion);
+ FeedbackRankQuestionDetails questionDetails =
+ (FeedbackRankQuestionDetails) feedbackQuestion.getQuestionDetails();
+ inputRankDetails(questionNum, questionDetails);
+ clickSaveNewQuestionButton();
+ }
+
+ public void editRankQuestion(int questionNum, FeedbackRankQuestionDetails questionDetails) {
+ clickEditQuestionButton(questionNum);
+ inputRankDetails(questionNum, questionDetails);
+ clickSaveQuestionButton(questionNum);
+ }
+
+ private String getCourseId() {
+ return courseIdTextBox.getText();
+ }
+
+ private String getCourseName() {
+ return courseNameTextBox.getText();
+ }
+
+ private String getTimeZone() {
+ return timezoneDropDown.getText();
+ }
+
+ private String getFeedbackSessionName() {
+ return sessionNameTextBox.getText();
+ }
+
+ private String getInstructions() {
+ return getEditorRichText(instructionsEditor.findElement(By.tagName("editor")));
+ }
+
+ private String getStartDate() {
+ return startDateBox.getAttribute("value");
+ }
+
+ private String getStartTime() {
+ return getSelectedDropdownOptionText(startTimeDropdown.findElement(By.tagName("select")));
+ }
+
+ private String getEndDate() {
+ return endDateBox.getAttribute("value");
+ }
+
+ private String getEndTime() {
+ return getSelectedDropdownOptionText(endTimeDropdown.findElement(By.tagName("select")));
+ }
+
+ private String getSessionVisibilityDate() {
+ return sessionVisibilityDateBox.getAttribute("value");
+ }
+
+ private String getSessionVisibilityTime() {
+ return getSelectedDropdownOptionText(sessionVisibilityTimeDropdown.findElement(By.tagName("select")));
+ }
+
+ private String getResponseVisibilityDate() {
+ return responseVisibilityDateBox.getAttribute("value");
+ }
+
+ private String getResponseVisibilityTime() {
+ return getSelectedDropdownOptionText(responseVisibilityTimeDropdown.findElement(By.tagName("select")));
+ }
+
+ private String getGracePeriod() {
+ return getSelectedDropdownOptionText(gracePeriodDropdown);
+ }
+
+ private String getSubmissionStatus() {
+ return submissionStatusTextBox.getText();
+ }
+
+ private String getPublishedStatus() {
+ return publishStatusTextBox.getText();
+ }
+
+ private String getDateString(Instant instant, ZoneId timeZone) {
+ return DateTimeFormatter
+ .ofPattern("EE, dd MMM, yyyy")
+ .format(instant.atZone(timeZone));
+ }
+
+ private String getTimeString(Instant instant, ZoneId timeZone) {
+ ZonedDateTime dateTime = instant.atZone(timeZone);
+ if (dateTime.getHour() == 23 && dateTime.getMinute() == 59) {
+ return "23:59H";
+ }
+ return DateTimeFormatter
+ .ofPattern("HH:00")
+ .format(instant.atZone(timeZone)) + "H";
+ }
+
+ private void setInstructions(String newInstructions) {
+ writeToRichTextEditor(instructionsEditor.findElement(By.tagName("editor")), newInstructions);
+ }
+
+ private void setSessionStartDateTime(Instant startInstant, ZoneId timeZone) {
+ setDateTime(startDateBox, startTimeDropdown, startInstant, timeZone);
+ }
+
+ private void setSessionEndDateTime(Instant endInstant, ZoneId timeZone) {
+ setDateTime(endDateBox, endTimeDropdown, endInstant, timeZone);
+ }
+
+ private void setVisibilityDateTime(Instant startInstant, ZoneId timeZone) {
+ setDateTime(sessionVisibilityDateBox, sessionVisibilityTimeDropdown, startInstant, timeZone);
+ }
+
+ private void setResponseDateTime(Instant endInstant, ZoneId timeZone) {
+ setDateTime(responseVisibilityDateBox, responseVisibilityTimeDropdown, endInstant, timeZone);
+ }
+
+ private void setDateTime(WebElement dateBox, WebElement timeBox, Instant startInstant, ZoneId timeZone) {
+ fillTextBox(dateBox, getDateString(startInstant, timeZone));
+
+ selectDropdownOptionByText(timeBox.findElement(By.tagName("select")), getTimeString(startInstant, timeZone));
+ }
+
+ private void selectGracePeriod(long gracePeriodMinutes) {
+ selectDropdownOptionByText(gracePeriodDropdown, gracePeriodMinutes + " min");
+ }
+
+ private void setVisibilitySettings(FeedbackSessionAttributes newFeedbackSession) {
+ showVisibilitySettings();
+
+ setSessionVisibilitySettings(newFeedbackSession);
+ setResponseVisibilitySettings(newFeedbackSession);
+ }
+
+ private void setSessionVisibilitySettings(FeedbackSessionAttributes newFeedbackSession) {
+ Instant sessionDateTime = newFeedbackSession.getSessionVisibleFromTime();
+ if (sessionDateTime.equals(Const.TIME_REPRESENTS_FOLLOW_OPENING)) {
+ click(openSessionVisibleTimeButton);
+ } else {
+ click(customSessionVisibleTimeButton);
+ setVisibilityDateTime(sessionDateTime, newFeedbackSession.getTimeZone());
+ }
+ }
+
+ private void setResponseVisibilitySettings(FeedbackSessionAttributes newFeedbackSession) {
+ Instant responseDateTime = newFeedbackSession.getResultsVisibleFromTime();
+ if (responseDateTime.equals(Const.TIME_REPRESENTS_FOLLOW_VISIBLE)) {
+ click(immediateResponseVisibleTimeButton);
+ } else if (responseDateTime.equals(Const.TIME_REPRESENTS_LATER)) {
+ click(manualResponseVisibleTimeButton);
+ } else {
+ click(customResponseVisibleTimeButton);
+ setResponseDateTime(responseDateTime, newFeedbackSession.getTimeZone());
+ }
+ }
+
+ private void setEmailSettings(FeedbackSessionAttributes newFeedbackSessionDetails) {
+ showEmailSettings();
+ if (newFeedbackSessionDetails.isOpeningEmailEnabled() != openingSessionEmailCheckbox.isSelected()) {
+ click(openingSessionEmailCheckbox);
+ }
+ if (newFeedbackSessionDetails.isClosingEmailEnabled() != closingSessionEmailCheckbox.isSelected()) {
+ click(closingSessionEmailCheckbox);
+ }
+ if (newFeedbackSessionDetails.isPublishedEmailEnabled() != publishedSessionEmailCheckbox.isSelected()) {
+ click(publishedSessionEmailCheckbox);
+ }
+ }
+
+ private void showVisibilitySettings() {
+ if (isElementPresent(By.id("btn-change-visibility"))) {
+ click(changeVisibilityButton);
+ }
+ }
+
+ private void showEmailSettings() {
+ if (isElementPresent(By.id("btn-change-email"))) {
+ click(changeEmailButton);
+ }
+ }
+
+ private int getNumQuestions() {
+ return browser.driver.findElements(By.tagName("tm-question-edit-form")).size();
+ }
+
+ private WebElement getQuestionForm(int questionNum) {
+ return browser.driver.findElements(By.tagName("tm-question-edit-form")).get(questionNum - 1);
+ }
+
+ private FeedbackQuestionType getQuestionType(int questionNum) {
+ String questionDetails = getQuestionForm(questionNum).findElement(By.id("question-header")).getText();
+ String questionType = questionDetails.split(" \\d+ ")[1].trim();
+
+ switch (questionType) {
+ case "Essay question":
+ return FeedbackQuestionType.TEXT;
+ case "Multiple-Choice (single answer) question":
+ return FeedbackQuestionType.MCQ;
+ case "Multiple-choice (multiple answers) question":
+ return FeedbackQuestionType.MSQ;
+ case "Numerical Scale Question":
+ return FeedbackQuestionType.NUMSCALE;
+ case "Distribute points (among options) question":
+ return FeedbackQuestionType.CONSTSUM_OPTIONS;
+ case "Distribute points (among recipients) question":
+ return FeedbackQuestionType.CONSTSUM_RECIPIENTS;
+ case "Team contribution question":
+ return FeedbackQuestionType.CONTRIB;
+ case "Rubric question":
+ return FeedbackQuestionType.RUBRIC;
+ case "Rank (options) question":
+ return FeedbackQuestionType.RANK_OPTIONS;
+ case "Rank (recipients) question":
+ return FeedbackQuestionType.RANK_RECIPIENTS;
+ default:
+ throw new IllegalArgumentException("Unknown FeedbackQuestionType");
+ }
+ }
+
+ private int getQuestionNumber(int questionNum) {
+ return Integer.parseInt(getQuestionForm(questionNum).findElement(By.id("question-number")).getText());
+ }
+
+ private String getQuestionBrief(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("question-brief")).getAttribute("value");
+ }
+
+ private String getQuestionDescription(int questionNum) {
+ WebElement editor = waitForElementPresence(By.cssSelector("#question-form-" + questionNum + " editor"));
+ return getEditorRichText(editor);
+ }
+
+ private String getFeedbackGiver(int questionNum) {
+ String feedbackPath = getFeedbackPath(questionNum);
+ if (feedbackPath.equals(CUSTOM_FEEDBACK_PATH_OPTION)) {
+ return getSelectedDropdownOptionText(getQuestionForm(questionNum).findElement(By.id("giver-type")));
+ }
+ return feedbackPath.split(FEEDBACK_PATH_SEPARATOR)[0];
+ }
+
+ private String getFeedbackReceiver(int questionNum) {
+ String feedbackPath = getFeedbackPath(questionNum);
+ if (feedbackPath.equals(CUSTOM_FEEDBACK_PATH_OPTION)) {
+ return getSelectedDropdownOptionText(getQuestionForm(questionNum).findElement(By.id("receiver-type")));
+ }
+ return feedbackPath.split(FEEDBACK_PATH_SEPARATOR)[1];
+ }
+
+ private String getFeedbackPath(int questionNum) {
+ WebElement questionForm = getQuestionForm(questionNum);
+ return questionForm.findElement(By.cssSelector("#btn-feedback-path span")).getText();
+ }
+
+ private void setQuestionBrief(int questionNum, String newBrief) {
+ fillTextBox(getQuestionForm(questionNum).findElement(By.id("question-brief")), newBrief);
+ }
+
+ private void setQuestionDescription(int questionNum, String newDescription) {
+ WebElement editor = waitForElementPresence(By.cssSelector("#question-form-" + questionNum + " editor"));
+ writeToRichTextEditor(editor, newDescription);
+ }
+
+ private void setFeedbackPath(int questionNum, FeedbackQuestionAttributes feedbackQuestion) {
+ FeedbackParticipantType newGiver = feedbackQuestion.getGiverType();
+ FeedbackParticipantType newRecipient = feedbackQuestion.getRecipientType();
+ String feedbackPath = getFeedbackPath(questionNum);
+ WebElement questionForm = getQuestionForm(questionNum);
+ if (!feedbackPath.equals(CUSTOM_FEEDBACK_PATH_OPTION)) {
+ selectFeedbackPathDropdownOption(questionNum, CUSTOM_FEEDBACK_PATH_OPTION + "...");
+ }
+ // Set to type STUDENT first to adjust NumberOfEntitiesToGiveFeedbackTo
+ selectDropdownOptionByText(questionForm.findElement(By.id("giver-type")),
+ FeedbackParticipantType.STUDENTS.toDisplayGiverName());
+ selectDropdownOptionByText(questionForm.findElement(By.id("receiver-type")),
+ FeedbackParticipantType.STUDENTS.toDisplayRecipientName());
+ if (feedbackQuestion.getNumberOfEntitiesToGiveFeedbackTo() == Const.MAX_POSSIBLE_RECIPIENTS) {
+ click(questionForm.findElement(By.id("unlimited-recipients")));
+ } else {
+ click(questionForm.findElement(By.id("custom-recipients")));
+ fillTextBox(questionForm.findElement(By.id("custom-recipients-number")),
+ Integer.toString(feedbackQuestion.getNumberOfEntitiesToGiveFeedbackTo()));
+ }
+
+ selectDropdownOptionByText(questionForm.findElement(By.id("giver-type")), newGiver.toDisplayGiverName());
+ selectDropdownOptionByText(questionForm.findElement(By.id("receiver-type")),
+ newRecipient.toDisplayRecipientName());
+ }
+
+ private void selectFeedbackPathDropdownOption(int questionNum, String text) {
+ WebElement questionForm = getQuestionForm(questionNum);
+ click(questionForm.findElement(By.id("btn-feedback-path")));
+ WebElement dropdown = questionForm.findElement(By.id("feedback-path-dropdown"));
+ List options = dropdown.findElements(By.className("dropdown-item"));
+ for (WebElement option : options) {
+ if (option.getText().equals(text)) {
+ click(option);
+ return;
+ }
+ }
+ }
+
+ private void clickEditQuestionButton(int questionNum) {
+ click(getQuestionForm(questionNum).findElement(By.id("btn-edit-question")));
+ }
+
+ private void clickSaveQuestionButton(int questionNum) {
+ WebElement saveButton = getQuestionForm(questionNum).findElement(By.id("btn-save-question"));
+ click(saveButton);
+ ThreadHelper.waitFor(1000);
+ }
+
+ private void setQuestionVisibility(int questionNum, FeedbackQuestionAttributes feedbackQuestion) {
+ WebElement questionForm = getQuestionForm(questionNum);
+ String visibility = questionForm.findElement(By.cssSelector("#btn-question-visibility span")).getText();
+ if (!visibility.equals(CUSTOM_VISIBILITY_OPTION)) {
+ selectVisibilityDropdownOption(questionNum, CUSTOM_VISIBILITY_OPTION + "...");
+ }
+
+ FeedbackParticipantType giver = feedbackQuestion.getGiverType();
+ FeedbackParticipantType receiver = feedbackQuestion.getRecipientType();
+ WebElement customVisibilityTable = questionForm.findElement(By.id("custom-visibility-table"));
+ selectVisibilityBoxes(customVisibilityTable, giver, receiver, feedbackQuestion.getShowResponsesTo(), 1);
+ selectVisibilityBoxes(customVisibilityTable, giver, receiver, feedbackQuestion.getShowGiverNameTo(), 2);
+ selectVisibilityBoxes(customVisibilityTable, giver, receiver, feedbackQuestion.getShowRecipientNameTo(), 3);
+ }
+
+ private void selectVisibilityBoxes(WebElement table, FeedbackParticipantType giver,
+ FeedbackParticipantType receiver, List participants,
+ int colNum) {
+ List possibleTypes = new ArrayList(Arrays.asList(FeedbackParticipantType.RECEIVER,
+ FeedbackParticipantType.OWN_TEAM_MEMBERS, FeedbackParticipantType.RECEIVER_TEAM_MEMBERS,
+ FeedbackParticipantType.STUDENTS, FeedbackParticipantType.INSTRUCTORS));
+ if (!giver.equals(FeedbackParticipantType.STUDENTS)) {
+ possibleTypes.remove(FeedbackParticipantType.OWN_TEAM_MEMBERS);
+ }
+ if (!receiver.equals(FeedbackParticipantType.STUDENTS)) {
+ possibleTypes.remove(FeedbackParticipantType.RECEIVER_TEAM_MEMBERS);
+ }
+ if (receiver.equals(FeedbackParticipantType.NONE)
+ || receiver.equals(FeedbackParticipantType.SELF)
+ || receiver.equals(FeedbackParticipantType.OWN_TEAM)) {
+ possibleTypes.remove(FeedbackParticipantType.RECEIVER);
+ possibleTypes.remove(FeedbackParticipantType.RECEIVER_TEAM_MEMBERS);
+ }
+
+ List rows = table.findElements(By.tagName("tr"));
+ int index = colNum - 1;
+ for (FeedbackParticipantType participant : participants) {
+ markOptionAsSelected(rows.get(possibleTypes.indexOf(participant)).findElements(By.tagName("input")).get(index));
+ }
+ }
+
+ private void selectVisibilityDropdownOption(int questionNum, String text) {
+ WebElement questionForm = getQuestionForm(questionNum);
+ click(questionForm.findElement(By.id("btn-question-visibility")));
+ WebElement dropdown = questionForm.findElement(By.id("question-visibility-dropdown"));
+ List options = dropdown.findElements(By.className("dropdown-item"));
+ for (WebElement option : options) {
+ if (option.getText().equals(text)) {
+ click(option);
+ return;
+ }
+ }
+ }
+
+ private void clickAndWaitForNewQuestion(WebElement button) {
+ int newQuestionNum = getNumQuestions() + 1;
+ click(button);
+ waitForElementPresence(By.id("question-form-" + newQuestionNum));
+ }
+
+ private void addNewQuestion(int optionNumber) {
+ click(addNewQuestionButton);
+ WebElement newQuestionDropdown = waitForElementPresence(By.id("new-question-dropdown"));
+ click(newQuestionDropdown.findElements(By.tagName("button")).get(optionNumber - 1));
+ }
+
+ private void clickSaveNewQuestionButton() {
+ WebElement saveButton = browser.driver.findElement(By.id("btn-save-new"));
+ click(saveButton);
+ waitForElementStaleness(saveButton);
+ }
+
+ private WebElement getRecommendedTextLengthField(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("recommended-length"));
+ }
+
+ private WebElement getGenerateOptionsCheckbox(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("generate-checkbox"));
+ }
+
+ private WebElement getGenerateOptionsDropdown(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("generate-dropdown"));
+ }
+
+ private WebElement getWeightCheckbox(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("weights-checkbox"));
+ }
+
+ private WebElement getOtherOptionCheckbox(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("other-checkbox"));
+ }
+
+ private String getGeneratedOptionString(FeedbackParticipantType type) {
+ switch (type) {
+ case STUDENTS:
+ return "students";
+ case STUDENTS_EXCLUDING_SELF:
+ return "students (excluding self)";
+ case TEAMS:
+ return "teams";
+ case TEAMS_EXCLUDING_SELF:
+ return "teams (excluding own team)";
+ case INSTRUCTORS:
+ return "instructors";
+ default:
+ return "unknown";
+ }
+ }
+
+ private String getDoubleString(Double value) {
+ return value % 1 == 0 ? Integer.toString(value.intValue()) : Double.toString(value);
+ }
+
+ private WebElement getOptionsSection(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("options-section"));
+ }
+
+ private List getOptionInputs(int questionNum) {
+ WebElement optionsSection = getOptionsSection(questionNum);
+ return optionsSection.findElements(By.cssSelector("input[type='text']"));
+ }
+
+ private List getOptionWeightInputs(int questionNum) {
+ WebElement optionsSection = getOptionsSection(questionNum);
+ return optionsSection.findElements(By.cssSelector("tm-weight-field input"));
+ }
+
+ private WebElement getOtherWeightInput(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("other-weight"));
+ }
+
+ private boolean verifyGeneratedOptions(int questionNum, FeedbackParticipantType participantType) {
+ if (!participantType.equals(FeedbackParticipantType.NONE)) {
+ assertTrue(getGenerateOptionsCheckbox(questionNum).isSelected());
+ assertEquals(getSelectedDropdownOptionText(getGenerateOptionsDropdown(questionNum)),
+ getGeneratedOptionString(participantType));
+ return true;
+ }
+ assertFalse(getGenerateOptionsCheckbox(questionNum).isSelected());
+ return false;
+ }
+
+ private void verifyOptions(int questionNum, List options) {
+ List inputs = getOptionInputs(questionNum);
+ for (int i = 0; i < options.size(); i++) {
+ assertEquals(options.get(i), inputs.get(i).getAttribute("value"));
+ }
+ }
+
+ private void verifyOptionWeights(int questionNum, boolean hasWeights, List weights) {
+ if (hasWeights) {
+ assertTrue(getWeightCheckbox(questionNum).isSelected());
+ List weightInputs = getOptionWeightInputs(questionNum);
+ for (int i = 0; i < weights.size(); i++) {
+ assertEquals(getDoubleString(weights.get(i)), weightInputs.get(i).getAttribute("value"));
+ }
+ } else {
+ assertFalse(getWeightCheckbox(questionNum).isSelected());
+ }
+ }
+
+ private void verifyOtherOption(int questionNum, boolean hasOther, Double weight) {
+ if (hasOther) {
+ assertTrue(getOtherOptionCheckbox(questionNum).isSelected());
+ if (weight > 0) {
+ String otherWeight = getOtherWeightInput(questionNum).getAttribute("value");
+ assertEquals(getDoubleString(weight), otherWeight);
+ }
+ } else {
+ assertFalse(getOtherOptionCheckbox(questionNum).isSelected());
+ }
+ }
+
+ private void inputMcqDetails(int questionNum, FeedbackMcqQuestionDetails questionDetails) {
+ if (inputGenerateOptions(questionNum, questionDetails.getGenerateOptionsFor())) {
+ return;
+ }
+
+ inputOptions(questionNum, questionDetails.getMcqChoices());
+ inputOptionWeights(questionNum, questionDetails.hasAssignedWeights(), questionDetails.getMcqWeights());
+ inputOtherChoice(questionNum, questionDetails.isOtherEnabled(), questionDetails.getMcqOtherWeight());
+ }
+
+ private boolean inputGenerateOptions(int questionNum, FeedbackParticipantType participantType) {
+ if (!participantType.equals(FeedbackParticipantType.NONE)) {
+ markOptionAsSelected(getGenerateOptionsCheckbox(questionNum));
+ selectDropdownOptionByText(getGenerateOptionsDropdown(questionNum),
+ getGeneratedOptionString(participantType));
+ clickSaveQuestionButton(questionNum);
+ return true;
+ }
+ markOptionAsUnselected(getGenerateOptionsCheckbox(questionNum));
+ return false;
+ }
+
+ private void inputOptions(int questionNum, List options) {
+ List inputs = getOptionInputs(questionNum);
+ int numInputsNeeded = options.size() - inputs.size();
+ if (numInputsNeeded > 0) {
+ for (int i = 0; i < numInputsNeeded; i++) {
+ click(getQuestionForm(questionNum).findElement(By.id("btn-add-option")));
+ }
+ inputs = getOptionInputs(questionNum);
+ }
+ if (numInputsNeeded < 0) {
+ for (int i = 0; i < -numInputsNeeded; i++) {
+ click(getOptionsSection(questionNum).findElement(By.tagName("button")));
+ }
+ inputs = getOptionInputs(questionNum);
+ }
+
+ for (int i = 0; i < options.size(); i++) {
+ fillTextBox(inputs.get(i), options.get(i));
+ }
+ }
+
+ private void inputOptionWeights(int questionNum, boolean hasWeights, List weights) {
+ if (hasWeights) {
+ markOptionAsSelected(getWeightCheckbox(questionNum));
+ List weightInputs = getOptionWeightInputs(questionNum);
+ for (int i = 0; i < weights.size(); i++) {
+ fillTextBox(weightInputs.get(i), getDoubleString(weights.get(i)));
+ }
+ } else {
+ markOptionAsUnselected(getWeightCheckbox(questionNum));
+ }
+ }
+
+ private void inputOtherChoice(int questionNum, boolean hasOther, Double otherWeight) {
+ if (hasOther) {
+ markOptionAsSelected(getOtherOptionCheckbox(questionNum));
+ if (otherWeight > 0) {
+ fillTextBox(getOtherWeightInput(questionNum), getDoubleString(otherWeight));
+ }
+ } else {
+ markOptionAsUnselected(getOtherOptionCheckbox(questionNum));
+ }
+ }
+
+ private WebElement getMaxOptionsCheckbox(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("max-options-checkbox"));
+ }
+
+ private WebElement getMaxOptionsInput(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("max-options"));
+ }
+
+ private WebElement getMinOptionsCheckbox(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("min-options-checkbox"));
+ }
+
+ private WebElement getMinOptionsInput(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("min-options"));
+ }
+
+ private void verifyMaxOptions(int questionNum, int maxOptions) {
+ if (maxOptions == Integer.MIN_VALUE) {
+ assertFalse(getMaxOptionsCheckbox(questionNum).isSelected());
+ } else {
+ assertTrue(getMaxOptionsCheckbox(questionNum).isSelected());
+ assertEquals(getMaxOptionsInput(questionNum).getAttribute("value"),
+ Integer.toString(maxOptions));
+ }
+ }
+
+ private void verifyMinOptions(int questionNum, int minOptions) {
+ if (minOptions == Integer.MIN_VALUE) {
+ assertFalse(getMinOptionsCheckbox(questionNum).isSelected());
+ } else {
+ assertTrue(getMinOptionsCheckbox(questionNum).isSelected());
+ assertEquals(getMinOptionsInput(questionNum).getAttribute("value"),
+ Integer.toString(minOptions));
+ }
+ }
+
+ private void inputMsqDetails(int questionNum, FeedbackMsqQuestionDetails questionDetails) {
+ if (inputGenerateOptions(questionNum, questionDetails.getGenerateOptionsFor())) {
+ return;
+ }
+
+ inputOptions(questionNum, questionDetails.getMsqChoices());
+ inputOptionWeights(questionNum, questionDetails.hasAssignedWeights(), questionDetails.getMsqWeights());
+ inputOtherChoice(questionNum, questionDetails.isOtherEnabled(), questionDetails.getMsqOtherWeight());
+ inputMaxOptions(questionNum, questionDetails.getMaxSelectableChoices());
+ inputMinOptions(questionNum, questionDetails.getMinSelectableChoices());
+ }
+
+ private void inputMaxOptions(int questionNum, int maxOptions) {
+ if (maxOptions == Integer.MIN_VALUE) {
+ markOptionAsUnselected(getMaxOptionsCheckbox(questionNum));
+ } else {
+ markOptionAsSelected(getMaxOptionsCheckbox(questionNum));
+ fillTextBox(getMaxOptionsInput(questionNum), Integer.toString(maxOptions));
+ }
+ }
+
+ private void inputMinOptions(int questionNum, int minOptions) {
+ if (minOptions == Integer.MIN_VALUE) {
+ markOptionAsUnselected(getMinOptionsCheckbox(questionNum));
+ } else {
+ markOptionAsSelected(getMinOptionsCheckbox(questionNum));
+ fillTextBox(getMinOptionsInput(questionNum), Integer.toString(minOptions));
+ }
+ }
+
+ private WebElement getMinNumscaleInput(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("min-value"));
+ }
+
+ private WebElement getMaxNumscaleInput(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("max-value"));
+ }
+
+ private WebElement getNumScaleIncrementInput(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("increment-value"));
+ }
+
+ private void inputNumScaleDetails(int questionNum, FeedbackNumericalScaleQuestionDetails questionDetails) {
+ inputNumScaleValue(getMinNumscaleInput(questionNum), Integer.toString(questionDetails.getMinScale()));
+ inputNumScaleValue(getNumScaleIncrementInput(questionNum), getDoubleString(questionDetails.getStep()));
+ inputNumScaleValue(getMaxNumscaleInput(questionNum), Integer.toString(questionDetails.getMaxScale()));
+ }
+
+ private void inputNumScaleValue(WebElement input, String value) {
+ input.clear();
+ input.sendKeys(value);
+ }
+
+ private WebElement getConstSumTotalPointsRadioBtn(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("total-points-radio"));
+ }
+
+ private WebElement getConstSumTotalPointsInput(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("total-points"));
+ }
+
+ private WebElement getConstSumPerOptionPointsRadioBtn(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("per-option-points-radio"));
+ }
+
+ private WebElement getConstSumPerOptionPointsInput(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("per-option-points"));
+ }
+
+ private WebElement getConstSumUnevenDistributionCheckbox(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("uneven-distribution-checkbox"));
+ }
+
+ private WebElement getConstSumUnevenDistributionDropdown(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("uneven-distribution-dropdown"));
+ }
+
+ private void inputConstSumDetails(int questionNum, FeedbackConstantSumQuestionDetails questionDetails) {
+ if (!questionDetails.isDistributeToRecipients()) {
+ inputOptions(questionNum, questionDetails.getConstSumOptions());
+ }
+ if (questionDetails.isPointsPerOption()) {
+ click(getConstSumPerOptionPointsRadioBtn(questionNum));
+ fillTextBox(getConstSumPerOptionPointsInput(questionNum), Integer.toString(questionDetails.getPoints()));
+ } else {
+ click(getConstSumTotalPointsRadioBtn(questionNum));
+ fillTextBox(getConstSumTotalPointsInput(questionNum), Integer.toString(questionDetails.getPoints()));
+ }
+ String distributeFor = questionDetails.getDistributePointsFor();
+ if (questionDetails.isForceUnevenDistribution()) {
+ markOptionAsSelected(getConstSumUnevenDistributionCheckbox(questionNum));
+ selectDropdownOptionByText(getConstSumUnevenDistributionDropdown(questionNum),
+ "All options".equals(distributeFor) ? "Every option" : distributeFor);
+ } else {
+ markOptionAsUnselected(getConstSumUnevenDistributionCheckbox(questionNum));
+ }
+ }
+
+ private WebElement getAllowNotSureContributionCheckbox(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("not-sure-checkbox"));
+ }
+
+ private void inputContributionDetails(int questionNum, FeedbackContributionQuestionDetails questionDetails) {
+ if (questionDetails.isNotSureAllowed()) {
+ markOptionAsSelected(getAllowNotSureContributionCheckbox(questionNum));
+ } else {
+ markOptionAsUnselected(getAllowNotSureContributionCheckbox(questionNum));
+ }
+ }
+
+ private WebElement getRubricRow(int questionNum, int rowNumber) {
+ return getQuestionForm(questionNum).findElements(By.cssSelector("tm-rubric-question-edit-details-form tr"))
+ .get(rowNumber - 1);
+ }
+
+ private List getRubricChoiceInputs(int questionNum) {
+ return getRubricRow(questionNum, 1).findElements(By.tagName("input"));
+ }
+
+ private List getRubricTextareas(int questionNum, int rowNum) {
+ return getRubricRow(questionNum, rowNum).findElements(By.tagName("textarea"));
+ }
+
+ private List getRubricWeights(int questionNum, int rowNum) {
+ return getRubricRow(questionNum, rowNum).findElements(By.tagName("input"));
+ }
+
+ private WebElement getRubricDeleteSubQnBtn(int questionNum, int rowNum) {
+ return getRubricRow(questionNum, rowNum).findElement(By.id("btn-delete-subquestion"));
+ }
+
+ private WebElement getRubricDeleteChoiceBtn(int questionNum, int colNum) {
+ return getRubricRow(questionNum, getNumRubricRows(questionNum)).findElements(By.id("btn-delete-choice")).get(colNum);
+ }
+
+ private int getNumRubricRows(int questionNum) {
+ return getQuestionForm(questionNum).findElements(By.cssSelector("#rubric-table tr")).size();
+ }
+
+ private int getNumRubricCols(int questionNum) {
+ return getRubricRow(questionNum, 1).findElements(By.tagName("td")).size();
+ }
+
+ private void inputRubricDetails(int questionNum, FeedbackRubricQuestionDetails questionDetails) {
+ int numSubQn = questionDetails.getNumOfRubricSubQuestions();
+ int numChoices = questionDetails.getNumOfRubricChoices();
+ adjustNumRubricFields(questionNum, numSubQn, numChoices);
+
+ List choices = questionDetails.getRubricChoices();
+ for (int i = 0; i < numChoices; i++) {
+ fillTextBox(getRubricChoiceInputs(questionNum).get(i), choices.get(i));
+ }
+
+ List subQuestions = questionDetails.getRubricSubQuestions();
+ List> descriptions = questionDetails.getRubricDescriptions();
+ for (int i = 0; i < numSubQn; i++) {
+ List textAreas = getRubricTextareas(questionNum, i + 2);
+ fillTextBox(textAreas.get(0), subQuestions.get(i));
+ for (int j = 0; j < numChoices; j++) {
+ if (descriptions.get(i).get(j).isEmpty()) {
+ // using clear does not work here
+ textAreas.get(j + 1).sendKeys(Keys.chord(Keys.CONTROL, "a"));
+ textAreas.get(j + 1).sendKeys(Keys.DELETE);
+ } else {
+ fillTextBox(textAreas.get(j + 1), descriptions.get(i).get(j));
+ }
+ }
+ }
+
+ if (questionDetails.hasAssignedWeights()) {
+ markOptionAsSelected(getWeightCheckbox(questionNum));
+ List> weights = questionDetails.getRubricWeights();
+ for (int i = 0; i < numSubQn; i++) {
+ for (int j = 0; j < numChoices; j++) {
+ fillTextBox(getRubricWeights(questionNum, i + 2).get(j), getDoubleString(weights.get(i).get(j)));
+ }
+ }
+ } else {
+ markOptionAsUnselected(getWeightCheckbox(questionNum));
+ }
+ }
+
+ private void adjustNumRubricFields(int questionNum, int numSubQn, int numChoices) {
+ int numSubQnsNeeded = numSubQn - (getNumRubricRows(questionNum) - 2);
+ int numChoicesNeeded = numChoices - (getNumRubricCols(questionNum) - 1);
+ if (numSubQnsNeeded > 0) {
+ for (int i = 0; i < numSubQnsNeeded; i++) {
+ click(getQuestionForm(questionNum).findElement(By.id("btn-add-row")));
+ }
+ }
+ if (numChoicesNeeded > 0) {
+ for (int i = 0; i < numChoicesNeeded; i++) {
+ click(getQuestionForm(questionNum).findElement(By.id("btn-add-col")));
+ }
+ }
+ if (numSubQnsNeeded < 0) {
+ for (int i = 0; i < -numSubQnsNeeded; i++) {
+ click(getRubricDeleteSubQnBtn(questionNum, 2));
+ }
+ }
+ if (numChoicesNeeded < 0) {
+ for (int i = 0; i < -numChoicesNeeded; i++) {
+ clickAndConfirm(getRubricDeleteChoiceBtn(questionNum, 2));
+ }
+ }
+ }
+
+ private WebElement getAllowDuplicateRankCheckbox(int questionNum) {
+ return getQuestionForm(questionNum).findElement(By.id("duplicate-rank-checkbox"));
+ }
+
+ private void inputRankDetails(int questionNum, FeedbackRankQuestionDetails questionDetails) {
+ if (questionDetails instanceof FeedbackRankOptionsQuestionDetails) {
+ FeedbackRankOptionsQuestionDetails optionDetails = (FeedbackRankOptionsQuestionDetails) questionDetails;
+ inputOptions(questionNum, optionDetails.getOptions());
+ }
+ if (questionDetails.areDuplicatesAllowed()) {
+ markOptionAsSelected(getAllowDuplicateRankCheckbox(questionNum));
+ } else {
+ markOptionAsUnselected(getAllowDuplicateRankCheckbox(questionNum));
+ }
+ inputMaxOptions(questionNum, questionDetails.getMaxOptionsToBeRanked());
+ inputMinOptions(questionNum, questionDetails.getMinOptionsToBeRanked());
+ }
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackSessionsPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackSessionsPage.java
new file mode 100644
index 00000000000..06f4f36918e
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackSessionsPage.java
@@ -0,0 +1,550 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.datatransfer.attributes.FeedbackSessionAttributes;
+import teammates.common.datatransfer.attributes.StudentAttributes;
+import teammates.common.util.Const;
+
+/**
+ * Represents the "Sessions" page for Instructors.
+ */
+public class InstructorFeedbackSessionsPage extends AppPage {
+
+ @FindBy(id = "btn-add-session")
+ private WebElement addSessionButton;
+
+ @FindBy(id = "session-type")
+ private WebElement sessionTypeDropdown;
+
+ @FindBy(id = "add-course-id")
+ private WebElement courseIdDropdown;
+
+ @FindBy(id = "add-session-name")
+ private WebElement sessionNameTextBox;
+
+ @FindBy(id = "instructions")
+ private WebElement instructionsEditor;
+
+ @FindBy(id = "submission-start-date")
+ private WebElement startDateBox;
+
+ @FindBy(id = "submission-start-time")
+ private WebElement startTimeDropdown;
+
+ @FindBy(id = "submission-end-date")
+ private WebElement endDateBox;
+
+ @FindBy(id = "submission-end-time")
+ private WebElement endTimeDropdown;
+
+ @FindBy(id = "grace-period")
+ private WebElement gracePeriodDropdown;
+
+ @FindBy(id = "btn-change-visibility")
+ private WebElement changeVisibilityButton;
+
+ @FindBy(id = "session-visibility-custom")
+ private WebElement customSessionVisibleTimeButton;
+
+ @FindBy(id = "session-visibility-date")
+ private WebElement sessionVisibilityDateBox;
+
+ @FindBy(id = "session-visibility-time")
+ private WebElement sessionVisibilityTimeDropdown;
+
+ @FindBy(id = "session-visibility-at-open")
+ private WebElement openSessionVisibleTimeButton;
+
+ @FindBy(id = "response-visibility-custom")
+ private WebElement customResponseVisibleTimeButton;
+
+ @FindBy(id = "response-visibility-date")
+ private WebElement responseVisibilityDateBox;
+
+ @FindBy(id = "response-visibility-time")
+ private WebElement responseVisibilityTimeDropdown;
+
+ @FindBy(id = "response-visibility-immediately")
+ private WebElement immediateResponseVisibleTimeButton;
+
+ @FindBy(id = "response-visibility-manually")
+ private WebElement manualResponseVisibleTimeButton;
+
+ @FindBy(id = "btn-change-email")
+ private WebElement changeEmailButton;
+
+ @FindBy(id = "email-opening")
+ private WebElement openingSessionEmailCheckbox;
+
+ @FindBy(id = "email-closing")
+ private WebElement closingSessionEmailCheckbox;
+
+ @FindBy(id = "email-published")
+ private WebElement publishedSessionEmailCheckbox;
+
+ @FindBy(id = "btn-create-session")
+ private WebElement createSessionButton;
+
+ @FindBy(id = "sessions-table")
+ private WebElement sessionsTable;
+
+ @FindBy(id = "deleted-sessions-heading")
+ private WebElement deleteTableHeading;
+
+ @FindBy(id = "btn-restore-all")
+ private WebElement restoreAllButton;
+
+ @FindBy(id = "btn-delete-all")
+ private WebElement deleteAllButton;
+
+ @FindBy(id = "deleted-sessions-table")
+ private WebElement deletedSessionsTable;
+
+ public InstructorFeedbackSessionsPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ return getPageTitle().contains("Feedback Sessions");
+ }
+
+ public void verifySessionsTable(FeedbackSessionAttributes[] sessions) {
+ String[][] expectedValues = new String[sessions.length][4];
+ for (int i = 0; i < sessions.length; i++) {
+ expectedValues[i] = getSessionDetails(sessions[i]);
+ }
+ verifyTableBodyValues(sessionsTable, expectedValues);
+ }
+
+ public void verifySessionDetails(FeedbackSessionAttributes session) {
+ String[] expectedValues = getSessionDetails(session);
+ int rowId = getFeedbackSessionRowId(session.getCourseId(), session.getFeedbackSessionName());
+ verifyTableRowValues(sessionsTable.findElements(By.cssSelector("tbody tr")).get(rowId), expectedValues);
+ }
+
+ public void verifySoftDeletedSessionsTable(FeedbackSessionAttributes[] sessions) {
+ showDeleteTable();
+ String[][] expectedValues = new String[sessions.length][4];
+ for (int i = 0; i < sessions.length; i++) {
+ expectedValues[i] = getSoftDeletedSessionDetails(sessions[i]);
+ }
+ verifyTableBodyValues(deletedSessionsTable, expectedValues);
+ }
+
+ public void verifyNumSoftDeleted(int expected) {
+ assertEquals(getNumSoftDeletedFeedbackSessions(), expected);
+ }
+
+ public void verifyResponseRate(FeedbackSessionAttributes session, String expectedResponseRate) {
+ int rowId = getFeedbackSessionRowId(session.getCourseId(), session.getFeedbackSessionName());
+ assertEquals(expectedResponseRate, getResponseRate(rowId));
+ }
+
+ public void addFeedbackSession(FeedbackSessionAttributes newSession, boolean isUsingTemplate) {
+ clickAddSessionButton();
+ waitForElementPresence(By.cssSelector("#instructions iframe"));
+
+ if (isUsingTemplate) {
+ selectDropdownOptionByText(sessionTypeDropdown, "session using template: team peer evaluation");
+ } else {
+ selectDropdownOptionByText(sessionTypeDropdown, "session with my own questions");
+ }
+ selectDropdownOptionByText(courseIdDropdown, newSession.getCourseId());
+ fillTextBox(sessionNameTextBox, newSession.getFeedbackSessionName());
+ setInstructions(newSession.getInstructions());
+ setSessionStartDateTime(newSession.getStartTime(), newSession.getTimeZone());
+ setSessionEndDateTime(newSession.getEndTime(), newSession.getTimeZone());
+ selectGracePeriod(newSession.getGracePeriodMinutes());
+ setVisibilitySettings(newSession);
+ setEmailSettings(newSession);
+
+ clickCreateSessionButton();
+ waitForSessionEditPage();
+ }
+
+ public void addCopyOfSession(FeedbackSessionAttributes sessionToCopy, CourseAttributes copyToCourse,
+ String newSessionName) {
+ clickAddSessionButton();
+ click(browser.driver.findElement(By.id("btn-copy-session")));
+
+ selectCourseToCopy(copyToCourse.getId());
+ fillTextBox(browser.driver.findElement(By.id("copy-session-name")), newSessionName);
+ selectSessionToCopy(sessionToCopy.getCourseId(), sessionToCopy.getFeedbackSessionName());
+
+ clickConfirmCopySessionButton();
+ waitForSessionEditPage();
+ }
+
+ public void copySession(FeedbackSessionAttributes sessionToCopy, CourseAttributes copyToCourse,
+ String newSessionName) {
+ String copyFromCourse = sessionToCopy.getCourseId();
+ String sessionName = sessionToCopy.getFeedbackSessionName();
+ WebElement copyFsModal = clickCopyButtonInTable(copyFromCourse, sessionName);
+
+ fillTextBox(copyFsModal.findElement(By.id("copy-session-name")), newSessionName);
+ selectCourseToCopyToInModal(copyFsModal, copyToCourse.getId());
+
+ click(browser.driver.findElement(By.id("btn-confirm-copy-course")));
+ }
+
+ public void moveToRecycleBin(FeedbackSessionAttributes sessionToDelete) {
+ int rowId = getFeedbackSessionRowId(sessionToDelete.getCourseId(), sessionToDelete.getFeedbackSessionName());
+ clickAndConfirm(browser.driver.findElement(By.id("btn-soft-delete-" + rowId)));
+ waitUntilAnimationFinish();
+ }
+
+ public void restoreSession(FeedbackSessionAttributes sessionToRestore) {
+ showDeleteTable();
+ int rowId = getSoftDeletedFeedbackSessionRowId(sessionToRestore.getCourseId(),
+ sessionToRestore.getFeedbackSessionName());
+ click(browser.driver.findElement(By.id("btn-restore-" + rowId)));
+ waitUntilAnimationFinish();
+ }
+
+ public void deleteSession(FeedbackSessionAttributes sessionToRestore) {
+ showDeleteTable();
+ int rowId = getSoftDeletedFeedbackSessionRowId(sessionToRestore.getCourseId(),
+ sessionToRestore.getFeedbackSessionName());
+ clickAndConfirm(browser.driver.findElement(By.id("btn-delete-" + rowId)));
+ waitUntilAnimationFinish();
+ }
+
+ public void restoreAllSessions() {
+ click(restoreAllButton);
+ waitUntilAnimationFinish();
+ }
+
+ public void deleteAllSessions() {
+ clickAndConfirm(deleteAllButton);
+ waitUntilAnimationFinish();
+ }
+
+ public void showDeleteTable() {
+ if (!isElementVisible(By.id("sort-deleted-course-id"))) {
+ click(deleteTableHeading);
+ waitUntilAnimationFinish();
+ }
+ }
+
+ public void sendReminderEmail(FeedbackSessionAttributes session, StudentAttributes student) {
+ int rowId = getFeedbackSessionRowId(session.getCourseId(), session.getFeedbackSessionName());
+
+ click(browser.driver.findElement(By.id("btn-remind-" + rowId)));
+ selectStudentToEmail(student.email);
+
+ click(browser.driver.findElement(By.id("btn-confirm-send-reminder")));
+ }
+
+ public void resendResultsLink(FeedbackSessionAttributes session, StudentAttributes student) {
+ int rowId = getFeedbackSessionRowId(session.getCourseId(), session.getFeedbackSessionName());
+
+ click(browser.driver.findElement(By.id("btn-results-" + rowId)));
+ click(waitForElementPresence(By.id("btn-resend-" + rowId)));
+ selectStudentToEmail(student.email);
+
+ click(browser.driver.findElement(By.id("btn-confirm-resend-results")));
+ }
+
+ public void publishSessionResults(FeedbackSessionAttributes sessionToPublish) {
+ int rowId = getFeedbackSessionRowId(sessionToPublish.getCourseId(), sessionToPublish.getFeedbackSessionName());
+ click(browser.driver.findElement(By.id("btn-results-" + rowId)));
+ clickAndConfirm(waitForElementPresence(By.id("btn-publish-" + rowId)));
+ }
+
+ public void unpublishSessionResults(FeedbackSessionAttributes sessionToPublish) {
+ int rowId = getFeedbackSessionRowId(sessionToPublish.getCourseId(), sessionToPublish.getFeedbackSessionName());
+ click(browser.driver.findElement(By.id("btn-results-" + rowId)));
+ clickAndConfirm(waitForElementPresence(By.id("btn-unpublish-" + rowId)));
+ }
+
+ public void downloadResults(FeedbackSessionAttributes session) {
+ int rowId = getFeedbackSessionRowId(session.getCourseId(), session.getFeedbackSessionName());
+ click(browser.driver.findElement(By.id("btn-results-" + rowId)));
+ click(waitForElementPresence(By.id("btn-download-" + rowId)));
+ }
+
+ public void sortBySessionsName() {
+ click(waitForElementPresence(By.id("sort-session-name")));
+ }
+
+ public void sortByCourseId() {
+ click(waitForElementPresence(By.id("sort-course-id")));
+ }
+
+ private String[] getSessionDetails(FeedbackSessionAttributes session) {
+ String[] details = new String[4];
+ details[0] = session.getCourseId();
+ details[1] = session.getFeedbackSessionName();
+ if (session.isClosed()) {
+ details[2] = "Closed";
+ } else if (session.isVisible() && (session.isOpened() || session.isInGracePeriod())) {
+ details[2] = "Open";
+ } else {
+ details[2] = "Awaiting";
+ }
+ details[3] = session.isPublished() ? "Published" : "Not Published";
+ return details;
+ }
+
+ private String[] getSoftDeletedSessionDetails(FeedbackSessionAttributes session) {
+ String[] details = new String[4];
+ details[0] = session.getCourseId();
+ details[1] = session.getFeedbackSessionName();
+ details[2] = getSimpleDateString(session.getCreatedTime(), session.getTimeZone());
+ details[3] = getSimpleDateString(session.getDeletedTime(), session.getTimeZone());
+ return details;
+ }
+
+ private String getDateString(Instant instant, ZoneId timeZone) {
+ return DateTimeFormatter
+ .ofPattern("EE, dd MMM, yyyy")
+ .format(instant.atZone(timeZone));
+ }
+
+ private String getSimpleDateString(Instant instant, ZoneId timeZone) {
+ return DateTimeFormatter
+ .ofPattern("dd MMM, yyyy")
+ .format(instant.atZone(timeZone));
+ }
+
+ private String getTimeString(Instant instant, ZoneId timeZone) {
+ ZonedDateTime dateTime = instant.atZone(timeZone);
+ if (dateTime.getHour() == 23 && dateTime.getMinute() == 59) {
+ return "23:59H";
+ }
+ return DateTimeFormatter
+ .ofPattern("HH:00")
+ .format(instant.atZone(timeZone)) + "H";
+ }
+
+ private String getResponseRate(int rowId) {
+ By showButtonId = By.id("show-response-rate-" + rowId);
+ if (isElementPresent(showButtonId)) {
+ click(showButtonId);
+ }
+ return waitForElementPresence(By.id("response-rate-" + rowId)).getText();
+ }
+
+ private void clickAddSessionButton() {
+ click(addSessionButton);
+ }
+
+ private void setInstructions(String newInstructions) {
+ writeToRichTextEditor(instructionsEditor, newInstructions);
+ }
+
+ private void setSessionStartDateTime(Instant startInstant, ZoneId timeZone) {
+ setDateTime(startDateBox, startTimeDropdown, startInstant, timeZone);
+ }
+
+ private void setSessionEndDateTime(Instant endInstant, ZoneId timeZone) {
+ setDateTime(endDateBox, endTimeDropdown, endInstant, timeZone);
+ }
+
+ private void setVisibilityDateTime(Instant startInstant, ZoneId timeZone) {
+ setDateTime(sessionVisibilityDateBox, sessionVisibilityTimeDropdown, startInstant, timeZone);
+ }
+
+ private void setResponseDateTime(Instant endInstant, ZoneId timeZone) {
+ setDateTime(responseVisibilityDateBox, responseVisibilityTimeDropdown, endInstant, timeZone);
+ }
+
+ private void setDateTime(WebElement dateBox, WebElement timeBox, Instant startInstant, ZoneId timeZone) {
+ fillTextBox(dateBox, getDateString(startInstant, timeZone));
+
+ selectDropdownOptionByText(timeBox.findElement(By.tagName("select")), getTimeString(startInstant, timeZone));
+ }
+
+ private void selectGracePeriod(long gracePeriodMinutes) {
+ selectDropdownOptionByText(gracePeriodDropdown, gracePeriodMinutes + " min");
+ }
+
+ private void setVisibilitySettings(FeedbackSessionAttributes newFeedbackSession) {
+ showVisibilitySettings();
+
+ setSessionVisibilitySettings(newFeedbackSession);
+ setResponseVisibilitySettings(newFeedbackSession);
+ }
+
+ private void setSessionVisibilitySettings(FeedbackSessionAttributes newFeedbackSession) {
+ Instant sessionDateTime = newFeedbackSession.getSessionVisibleFromTime();
+ if (sessionDateTime.equals(Const.TIME_REPRESENTS_FOLLOW_OPENING)) {
+ click(openSessionVisibleTimeButton);
+ } else {
+ click(customSessionVisibleTimeButton);
+ setVisibilityDateTime(sessionDateTime, newFeedbackSession.getTimeZone());
+ }
+ }
+
+ private void setResponseVisibilitySettings(FeedbackSessionAttributes newFeedbackSession) {
+ Instant responseDateTime = newFeedbackSession.getResultsVisibleFromTime();
+ if (responseDateTime.equals(Const.TIME_REPRESENTS_FOLLOW_VISIBLE)) {
+ click(immediateResponseVisibleTimeButton);
+ } else if (responseDateTime.equals(Const.TIME_REPRESENTS_LATER)) {
+ click(manualResponseVisibleTimeButton);
+ } else {
+ click(customResponseVisibleTimeButton);
+ setResponseDateTime(responseDateTime, newFeedbackSession.getTimeZone());
+ }
+ }
+
+ private void setEmailSettings(FeedbackSessionAttributes newFeedbackSessionDetails) {
+ showEmailSettings();
+ if (newFeedbackSessionDetails.isOpeningEmailEnabled() != openingSessionEmailCheckbox.isSelected()) {
+ click(openingSessionEmailCheckbox);
+ }
+ if (newFeedbackSessionDetails.isClosingEmailEnabled() != closingSessionEmailCheckbox.isSelected()) {
+ click(closingSessionEmailCheckbox);
+ }
+ if (newFeedbackSessionDetails.isPublishedEmailEnabled() != publishedSessionEmailCheckbox.isSelected()) {
+ click(publishedSessionEmailCheckbox);
+ }
+ }
+
+ private void showVisibilitySettings() {
+ if (isElementPresent(By.id("btn-change-visibility"))) {
+ click(changeVisibilityButton);
+ }
+ }
+
+ private void showEmailSettings() {
+ if (isElementPresent(By.id("btn-change-email"))) {
+ click(changeEmailButton);
+ }
+ }
+
+ private void clickCreateSessionButton() {
+ click(createSessionButton);
+ }
+
+ private void selectCourseToCopy(String courseToCopyId) {
+ WebElement courseIdDropdown = waitForElementPresence(By.id("copy-course-id"));
+ selectDropdownOptionByText(courseIdDropdown, courseToCopyId);
+ }
+
+ private void selectSessionToCopy(String copyFromCourse, String sessionNameToCopy) {
+ WebElement table = browser.driver.findElement(By.id("copy-selection-table"));
+ List rows = table.findElements(By.tagName("tr"));
+
+ for (WebElement row : rows) {
+ List cells = row.findElements(By.tagName("td"));
+ if (cells.isEmpty()) {
+ continue;
+ }
+ if (cells.get(1).getText().equals(copyFromCourse) && cells.get(2).getText().equals(sessionNameToCopy)) {
+ click(cells.get(0).findElement(By.tagName("input")));
+ break;
+ }
+ }
+ }
+
+ private void clickConfirmCopySessionButton() {
+ click(browser.driver.findElement(By.id("btn-confirm-copy")));
+ }
+
+ private WebElement clickCopyButtonInTable(String courseId, String sessionName) {
+ int rowId = getFeedbackSessionRowId(courseId, sessionName);
+ click(browser.driver.findElement(By.id("btn-copy-" + rowId)));
+ return waitForElementPresence(By.id("copy-course-modal"));
+ }
+
+ private void selectCourseToCopyToInModal(WebElement copyFsModal, String courseToCopyId) {
+ List options = copyFsModal.findElements(By.className("form-check"));
+ for (WebElement option : options) {
+ String courseId = option.findElement(By.cssSelector("label span")).getText();
+ if (courseId.equals(courseToCopyId)) {
+ click(option.findElement(By.tagName("input")));
+ break;
+ }
+ }
+ }
+
+ private void selectStudentToEmail(String studentEmail) {
+ WebElement studentList = waitForElementPresence(By.id("student-list-table"));
+
+ List rows = studentList.findElements(By.tagName("tr"));
+ for (WebElement row : rows) {
+ List cells = row.findElements(By.cssSelector("td"));
+ if (cells.isEmpty()) {
+ continue;
+ }
+ if (cells.get(4).getText().equals(studentEmail)) {
+ click(cells.get(0).findElement(By.tagName("input")));
+ break;
+ }
+ }
+ }
+
+ private int getFeedbackSessionRowId(String courseId, String sessionName) {
+ int i = 0;
+ while (i < getNumFeedbackSessions()) {
+ if (getFeedbackSessionCourseId(i).equals(courseId)
+ && getFeedbackSessionName(i).equals(sessionName)) {
+ return i;
+ }
+ i++;
+ }
+ return -1;
+ }
+
+ private int getSoftDeletedFeedbackSessionRowId(String courseId, String sessionName) {
+ int i = 0;
+
+ while (i < getNumSoftDeletedFeedbackSessions()) {
+ if (getSoftDeletedFeedbackSessionCourseId(i).equals(courseId)
+ && getSoftDeletedFeedbackSessionName(i).equals(sessionName)) {
+ return i;
+ }
+ i++;
+ }
+ return -1;
+ }
+
+ private int getNumFeedbackSessions() {
+ return sessionsTable.findElements(By.cssSelector("tbody tr")).size();
+ }
+
+ private int getNumSoftDeletedFeedbackSessions() {
+ if (!isElementPresent(By.id("deleted-sessions-table"))) {
+ return 0;
+ }
+ return deletedSessionsTable.findElements(By.cssSelector("tbody tr")).size();
+ }
+
+ private String getFeedbackSessionCourseId(int rowId) {
+ WebElement row = sessionsTable.findElements(By.cssSelector("tbody tr")).get(rowId);
+ return row.findElements(By.tagName("td")).get(0).getText();
+ }
+
+ private String getSoftDeletedFeedbackSessionCourseId(int rowId) {
+ WebElement row = deletedSessionsTable.findElements(By.cssSelector("tbody tr")).get(rowId);
+ return row.findElements(By.tagName("td")).get(0).getText();
+ }
+
+ private String getFeedbackSessionName(int rowId) {
+ WebElement row = sessionsTable.findElements(By.cssSelector("tbody tr")).get(rowId);
+ return row.findElements(By.tagName("td")).get(1).getText();
+ }
+
+ private String getSoftDeletedFeedbackSessionName(int rowId) {
+ WebElement row = deletedSessionsTable.findElements(By.cssSelector("tbody tr")).get(rowId);
+ return row.findElements(By.tagName("td")).get(1).getText();
+ }
+
+ private void waitForSessionEditPage() {
+ waitForElementPresence(By.id("btn-fs-edit"));
+ }
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorHomePage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorHomePage.java
index 9d3193f81d7..46c4c02e4ac 100644
--- a/src/e2e/java/teammates/e2e/pageobjects/InstructorHomePage.java
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorHomePage.java
@@ -1,9 +1,32 @@
package teammates.e2e.pageobjects;
+import static org.junit.Assert.assertEquals;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.datatransfer.attributes.FeedbackSessionAttributes;
+import teammates.common.datatransfer.attributes.StudentAttributes;
+
/**
* Represents the instructor home page.
*/
public class InstructorHomePage extends AppPage {
+
+ @FindBy(id = "search-input")
+ private WebElement searchBar;
+
+ @FindBy(id = "btn-search")
+ private WebElement searchButton;
+
public InstructorHomePage(Browser browser) {
super(browser);
}
@@ -12,4 +35,196 @@ public InstructorHomePage(Browser browser) {
protected boolean containsExpectedPageContents() {
return getPageTitle().contains("Home");
}
+
+ public InstructorSearchPage searchKeyword(String keyword) {
+ fillTextBox(searchBar, keyword);
+ click(searchButton);
+ waitForPageToLoad(true);
+ return changePageType(InstructorSearchPage.class);
+ }
+
+ public void verifyCourseTabDetails(int courseTabIndex, CourseAttributes course, FeedbackSessionAttributes[] sessions) {
+ String expectedDetails = "[" + course.getId() + "]: " + course.getName();
+ assertEquals(getCourseDetails(courseTabIndex), expectedDetails);
+
+ String[][] expectedValues = new String[sessions.length][5];
+ for (int i = 0; i < sessions.length; i++) {
+ expectedValues[i] = getExpectedSessionDetails(sessions[i]);
+ }
+ verifyTableBodyValues(getSessionsTable(courseTabIndex), expectedValues);
+ }
+
+ public void verifySessionDetails(int courseTabIndex, int sessionIndex, FeedbackSessionAttributes session) {
+ String[] expectedValues = getExpectedSessionDetails(session);
+ WebElement sessionRow = getSessionsTable(courseTabIndex).findElements(By.cssSelector("tbody tr")).get(sessionIndex);
+ verifyTableRowValues(sessionRow, expectedValues);
+ }
+
+ public void verifyNumCourses(int expectedNum) {
+ assertEquals(getNumCourses(), expectedNum);
+ }
+
+ public void verifyResponseRate(int courseTabIndex, int sessionIndex, String expectedResponseRate) {
+ assertEquals(expectedResponseRate, getResponseRate(courseTabIndex, sessionIndex));
+ }
+
+ public void copySession(int courseTabIndex, int sessionIndex, CourseAttributes copyToCourse, String newSessionName) {
+ WebElement copyFsModal = clickCopyButtonInTable(courseTabIndex, sessionIndex);
+ fillTextBox(copyFsModal.findElement(By.id("copy-session-name")), newSessionName);
+ selectCourseToCopyToInModal(copyFsModal, copyToCourse.getId());
+ click(browser.driver.findElement(By.id("btn-confirm-copy-course")));
+ }
+
+ public void publishSessionResults(int courseTabIndex, int sessionIndex) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ click(courseTab.findElement(By.id("btn-results-" + sessionIndex)));
+ clickAndConfirm(courseTab.findElement(By.id("btn-publish-" + sessionIndex)));
+ }
+
+ public void unpublishSessionResults(int courseTabIndex, int sessionIndex) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ click(courseTab.findElement(By.id("btn-results-" + sessionIndex)));
+ clickAndConfirm(courseTab.findElement(By.id("btn-unpublish-" + sessionIndex)));
+ }
+
+ public void sendReminderEmail(int courseTabIndex, int sessionIndex, StudentAttributes student) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ click(courseTab.findElement(By.id("btn-remind-" + sessionIndex)));
+ selectStudentToEmail(student.email);
+ click(browser.driver.findElement(By.id("btn-confirm-send-reminder")));
+ }
+
+ public void resendResultsLink(int courseTabIndex, int sessionIndex, StudentAttributes student) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ click(courseTab.findElement(By.id("btn-results-" + sessionIndex)));
+ click(waitForElementPresence(By.id("btn-resend-" + sessionIndex)));
+ selectStudentToEmail(student.email);
+ click(browser.driver.findElement(By.id("btn-confirm-resend-results")));
+ }
+
+ public void downloadResults(int courseTabIndex, int sessionIndex) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ click(courseTab.findElement(By.id("btn-results-" + sessionIndex)));
+ click(waitForElementPresence(By.id("btn-download-" + sessionIndex)));
+ }
+
+ public void deleteSession(int courseTabIndex, int sessionIndex) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ clickAndConfirm(courseTab.findElement(By.id("btn-soft-delete-" + sessionIndex)));
+ waitUntilAnimationFinish();
+ }
+
+ public void archiveCourse(int courseTabIndex) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ click(courseTab.findElement(By.id("btn-course")));
+ clickAndConfirm(courseTab.findElement(By.id("btn-archive-course")));
+ waitUntilAnimationFinish();
+ }
+
+ public void deleteCourse(int courseTabIndex) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ click(courseTab.findElement(By.id("btn-course")));
+ clickAndConfirm(courseTab.findElement(By.id("btn-delete-course")));
+ waitUntilAnimationFinish();
+ }
+
+ public void sortCoursesById() {
+ click(browser.driver.findElement(By.id("sort-course-id")));
+ waitUntilAnimationFinish();
+ }
+
+ public void sortCoursesByName() {
+ click(browser.driver.findElement(By.id("sort-course-name")));
+ waitUntilAnimationFinish();
+ }
+
+ public void sortCoursesByCreationDate() {
+ click(browser.driver.findElement(By.id("sort-course-date")));
+ waitUntilAnimationFinish();
+ }
+
+ private int getNumCourses() {
+ return browser.driver.findElements(By.id("course-tab")).size();
+ }
+
+ private WebElement getCourseTab(int courseTabIndex) {
+ return browser.driver.findElements(By.id("course-tab")).get(courseTabIndex);
+ }
+
+ private String getCourseDetails(int courseTabIndex) {
+ WebElement courseTab = getCourseTab(courseTabIndex);
+ return courseTab.findElement(By.id("course-details")).getText();
+ }
+
+ private WebElement getSessionsTable(int courseTabIndex) {
+ return getCourseTab(courseTabIndex).findElement(By.id("sessions-table"));
+ }
+
+ private String getDateString(Instant instant, ZoneId timeZone) {
+ return DateTimeFormatter
+ .ofPattern("d MMM h:mm a")
+ .format(instant.atZone(timeZone));
+ }
+
+ private String[] getExpectedSessionDetails(FeedbackSessionAttributes session) {
+ String[] details = new String[5];
+ details[0] = session.getFeedbackSessionName();
+ details[1] = getDateString(session.getStartTime(), session.getTimeZone());
+ details[2] = getDateString(session.getEndTime(), session.getTimeZone());
+
+ if (session.isClosed()) {
+ details[3] = "Closed";
+ } else if (session.isVisible() && (session.isOpened() || session.isInGracePeriod())) {
+ details[3] = "Open";
+ } else {
+ details[3] = "Awaiting";
+ }
+ details[4] = session.isPublished() ? "Published" : "Not Published";
+ return details;
+ }
+
+ private String getResponseRate(int courseTabIndex, int sessionIndex) {
+ WebElement showButton = null;
+ try {
+ showButton = getCourseTab(courseTabIndex).findElement(By.id("show-response-rate-" + sessionIndex));
+ } catch (NoSuchElementException e) {
+ // continue
+ }
+ if (showButton != null) {
+ click(showButton);
+ }
+ return waitForElementPresence(By.id("response-rate-" + sessionIndex)).getText();
+ }
+
+ private WebElement clickCopyButtonInTable(int courseTabIndex, int sessionIndex) {
+ click(getCourseTab(courseTabIndex).findElement(By.id("btn-copy-" + sessionIndex)));
+ return waitForElementPresence(By.id("copy-course-modal"));
+ }
+
+ private void selectCourseToCopyToInModal(WebElement copyFsModal, String courseToCopyId) {
+ List options = copyFsModal.findElements(By.className("form-check"));
+ for (WebElement option : options) {
+ String courseId = option.findElement(By.cssSelector("label span")).getText();
+ if (courseId.equals(courseToCopyId)) {
+ click(option.findElement(By.tagName("input")));
+ break;
+ }
+ }
+ }
+
+ private void selectStudentToEmail(String studentEmail) {
+ WebElement studentList = waitForElementPresence(By.id("student-list-table"));
+
+ List rows = studentList.findElements(By.tagName("tr"));
+ for (WebElement row : rows) {
+ List cells = row.findElements(By.cssSelector("td"));
+ if (cells.isEmpty()) {
+ continue;
+ }
+ if (cells.get(4).getText().equals(studentEmail)) {
+ click(cells.get(0).findElement(By.tagName("input")));
+ break;
+ }
+ }
+ }
}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorSearchPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorSearchPage.java
new file mode 100644
index 00000000000..f6d71608b76
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorSearchPage.java
@@ -0,0 +1,173 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.datatransfer.attributes.StudentAttributes;
+import teammates.common.util.ThreadHelper;
+
+/**
+ * Represents the instructor search page.
+ */
+public class InstructorSearchPage extends AppPage {
+
+ @FindBy(id = "search-keyword")
+ private WebElement searchKeyword;
+
+ @FindBy(id = "btn-search")
+ private WebElement searchButton;
+
+ @FindBy(id = "students-checkbox")
+ private WebElement studentsCheckbox;
+
+ @FindBy(id = "comment-checkbox")
+ private WebElement commentCheckbox;
+
+ public InstructorSearchPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ return getPageTitle().contains("Search");
+ }
+
+ public void verifyNumCoursesInStudentResults(int expectedNum) {
+ List studentCoursesResult = getStudentCoursesResult();
+ assertEquals(expectedNum, studentCoursesResult.size());
+ }
+
+ public void search(boolean searchForStudents, boolean searchForComments, String searchTerm) {
+ if (searchForStudents && !studentsCheckbox.isSelected()
+ || !searchForStudents && studentsCheckbox.isSelected()) {
+ click(studentsCheckbox);
+ }
+
+ if (searchForComments && !commentCheckbox.isSelected()
+ || !searchForComments && commentCheckbox.isSelected()) {
+ click(commentCheckbox);
+ }
+
+ if (searchForStudents || searchForComments) {
+ searchKeyword.clear();
+ searchKeyword.sendKeys(searchTerm);
+ click(searchButton);
+ waitForPageToLoad(true);
+ waitUntilAnimationFinish();
+ } else {
+ verifyUnclickable(searchButton);
+ }
+ }
+
+ private List getStudentCoursesResult() {
+ return browser.driver.findElements(By.className("student-course-table"));
+ }
+
+ private String createHeaderText(CourseAttributes course) {
+ return "[" + course.getId() + "]";
+ }
+
+ public void verifyStudentDetails(Map courses, Map students) {
+ List studentCoursesResult = getStudentCoursesResult();
+ assertEquals(students.size(), courses.size());
+ assertEquals(students.size(), studentCoursesResult.size());
+
+ students.forEach((courseId, studentsForCourse) -> verifyStudentDetails(courses.get(courseId), studentsForCourse));
+ }
+
+ public void verifyStudentDetails(CourseAttributes course, StudentAttributes[] students) {
+ WebElement targetCourse = getStudentTableForHeader(course);
+ if (targetCourse == null) {
+ fail("Course with ID " + course.getId() + " is not found");
+ }
+
+ WebElement studentList = targetCourse.findElement(By.tagName("table"));
+ verifyTableBodyValues(studentList, getExpectedStudentValues(students));
+ }
+
+ private WebElement getStudentTableForHeader(CourseAttributes course) {
+ String targetHeader = createHeaderText(course);
+ List studentCoursesResult = getStudentCoursesResult();
+
+ return studentCoursesResult.stream().filter(studentCourse -> {
+ String courseHeader = studentCourse.findElement(By.className("card-header")).getText();
+ return targetHeader.equals(courseHeader);
+ }).findFirst().orElse(null);
+ }
+
+ private String[][] getExpectedStudentValues(StudentAttributes[] students) {
+ String[][] expected = new String[students.length][6];
+ for (int i = 0; i < students.length; i++) {
+ StudentAttributes student = students[i];
+ expected[i][0] = "View Photo";
+ expected[i][1] = student.getSection();
+ expected[i][2] = student.getTeam();
+ expected[i][3] = student.getName();
+ expected[i][4] = student.getGoogleId().isEmpty() ? "Yet to Join" : "Joined";
+ expected[i][5] = student.getEmail();
+ }
+ return expected;
+ }
+
+ public void deleteStudent(CourseAttributes course, String studentEmail) {
+ clickAndConfirm(getDeleteButton(course, studentEmail));
+ waitUntilAnimationFinish();
+ }
+
+ private WebElement getDeleteButton(CourseAttributes course, String studentEmail) {
+ WebElement studentRow = getStudentRow(course, studentEmail);
+ return studentRow.findElement(By.id("btn-delete"));
+ }
+
+ private WebElement getStudentRow(CourseAttributes course, String studentEmail) {
+ WebElement targetCourse = getStudentTableForHeader(course);
+ if (targetCourse == null) {
+ fail("Course with ID " + course.getId() + " is not found");
+ }
+
+ List studentRows = targetCourse.findElements(By.cssSelector("tbody tr"));
+ for (WebElement studentRow : studentRows) {
+ List studentCells = studentRow.findElements(By.tagName("td"));
+ if (studentCells.get(5).getText().equals(studentEmail)) {
+ return studentRow;
+ }
+ }
+ return null;
+ }
+
+ public InstructorCourseStudentDetailsViewPage clickViewStudent(CourseAttributes course, String studentEmail) {
+ WebElement studentRow = getStudentRow(course, studentEmail);
+ WebElement viewButton = studentRow.findElement(By.id("btn-view-details"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorCourseStudentDetailsViewPage.class);
+ }
+
+ public InstructorCourseStudentDetailsEditPage clickEditStudent(CourseAttributes course, String studentEmail) {
+ WebElement studentRow = getStudentRow(course, studentEmail);
+ WebElement viewButton = studentRow.findElement(By.id("btn-edit-details"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorCourseStudentDetailsEditPage.class);
+ }
+
+ public InstructorStudentRecordsPage clickViewAllRecords(CourseAttributes course, String studentEmail) {
+ WebElement studentRow = getStudentRow(course, studentEmail);
+ WebElement viewButton = studentRow.findElement(By.id("btn-view-records"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorStudentRecordsPage.class);
+ }
+
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorStudentListPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorStudentListPage.java
new file mode 100644
index 00000000000..c8245df9881
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorStudentListPage.java
@@ -0,0 +1,185 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.datatransfer.attributes.StudentAttributes;
+import teammates.common.util.ThreadHelper;
+
+/**
+ * Page Object Model for instructor student list page.
+ */
+public class InstructorStudentListPage extends AppPage {
+
+ public InstructorStudentListPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ return getPageSource().contains("Student List");
+ }
+
+ private List getCoursesTabs() {
+ return browser.driver.findElements(By.className("course-table"));
+ }
+
+ private String createHeaderText(CourseAttributes course) {
+ return String.format("[%s]: %s", course.getId(), course.getName());
+ }
+
+ public void clickCourseTabHeader(CourseAttributes course) {
+ String targetHeader = createHeaderText(course);
+ List courseTabs = getCoursesTabs();
+ for (WebElement courseTab : courseTabs) {
+ WebElement headerElement = courseTab.findElement(By.className("card-header"));
+ String header = headerElement.getText();
+ if (header.equals(targetHeader)) {
+ click(headerElement);
+ waitForPageToLoad();
+ waitUntilAnimationFinish();
+ }
+ }
+ }
+
+ public void verifyStudentDetails(Map courses, Map students) {
+ List coursesTabs = getCoursesTabs();
+ assertEquals(students.size(), courses.size());
+ assertEquals(students.size(), coursesTabs.size());
+
+ students.forEach((courseId, studentsForCourse) -> verifyStudentDetails(courses.get(courseId), studentsForCourse));
+ }
+
+ public void verifyStudentDetails(CourseAttributes course, StudentAttributes[] students) {
+ WebElement targetCourse = getCourseTab(course);
+ if (targetCourse == null) {
+ fail("Course with ID " + course.getId() + " is not found");
+ }
+
+ if (students.length == 0) {
+ String noStudentText = targetCourse.findElement(By.className("card-body")).getText();
+ // Need to account for the text from the enroll students button as well
+ String expectedText = "There are no students in this course."
+ + System.lineSeparator() + "Enroll Students";
+ assertEquals(expectedText, noStudentText);
+ } else {
+ WebElement studentList = targetCourse.findElement(By.tagName("table"));
+ verifyTableBodyValues(studentList, getExpectedStudentValues(students));
+ verifyDisplayedNumbers(targetCourse, students);
+ }
+ }
+
+ private WebElement getCourseTab(CourseAttributes course) {
+ String targetHeader = createHeaderText(course);
+ List courseTabs = getCoursesTabs();
+
+ return courseTabs.stream().filter(courseTab -> {
+ String courseHeader = courseTab.findElement(By.className("card-header")).getText();
+ return targetHeader.equals(courseHeader);
+ }).findFirst().orElse(null);
+ }
+
+ private void verifyDisplayedNumbers(WebElement courseTab, StudentAttributes[] students) {
+ String nStudents = courseTab.findElement(By.id("num-students")).getText();
+ String nSections = courseTab.findElement(By.id("num-sections")).getText();
+ String nTeams = courseTab.findElement(By.id("num-teams")).getText();
+
+ String expectedNStudents = students.length + " students";
+ String expectedNSections = Arrays.stream(students)
+ .map(StudentAttributes::getSection)
+ .distinct()
+ .count() + " sections";
+ String expectedNTeams = Arrays.stream(students)
+ .map(StudentAttributes::getTeam)
+ .distinct()
+ .count() + " teams";
+
+ assertEquals(expectedNStudents, nStudents);
+ assertEquals(expectedNSections, nSections);
+ assertEquals(expectedNTeams, nTeams);
+ }
+
+ private String[][] getExpectedStudentValues(StudentAttributes[] students) {
+ String[][] expected = new String[students.length][6];
+ for (int i = 0; i < students.length; i++) {
+ StudentAttributes student = students[i];
+ expected[i][0] = "View Photo";
+ expected[i][1] = student.getSection();
+ expected[i][2] = student.getTeam();
+ expected[i][3] = student.getName();
+ expected[i][4] = student.getGoogleId().isEmpty() ? "Yet to Join" : "Joined";
+ expected[i][5] = student.getEmail();
+ }
+ return expected;
+ }
+
+ public void deleteStudent(CourseAttributes course, String studentEmail) {
+ clickAndConfirm(getDeleteButton(course, studentEmail));
+ waitUntilAnimationFinish();
+ }
+
+ private WebElement getDeleteButton(CourseAttributes course, String studentEmail) {
+ WebElement studentRow = getStudentRow(course, studentEmail);
+ return studentRow.findElement(By.id("btn-delete"));
+ }
+
+ private WebElement getStudentRow(CourseAttributes course, String studentEmail) {
+ WebElement targetCourse = getCourseTab(course);
+ if (targetCourse == null) {
+ fail("Course with ID " + course.getId() + " is not found");
+ }
+
+ List studentRows = targetCourse.findElements(By.cssSelector("tbody tr"));
+ for (WebElement studentRow : studentRows) {
+ List studentCells = studentRow.findElements(By.tagName("td"));
+ if (studentCells.get(5).getText().equals(studentEmail)) {
+ return studentRow;
+ }
+ }
+ return null;
+ }
+
+ public InstructorCourseEnrollPage clickEnrollStudents(CourseAttributes course) {
+ WebElement studentRow = getCourseTab(course);
+ WebElement enrollButton = studentRow.findElement(By.id("btn-enroll"));
+ click(enrollButton);
+ waitForPageToLoad();
+ return changePageType(InstructorCourseEnrollPage.class);
+ }
+
+ public InstructorCourseStudentDetailsViewPage clickViewStudent(CourseAttributes course, String studentEmail) {
+ WebElement studentRow = getStudentRow(course, studentEmail);
+ WebElement viewButton = studentRow.findElement(By.id("btn-view-details"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorCourseStudentDetailsViewPage.class);
+ }
+
+ public InstructorCourseStudentDetailsEditPage clickEditStudent(CourseAttributes course, String studentEmail) {
+ WebElement studentRow = getStudentRow(course, studentEmail);
+ WebElement viewButton = studentRow.findElement(By.id("btn-edit-details"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorCourseStudentDetailsEditPage.class);
+ }
+
+ public InstructorStudentRecordsPage clickViewAllRecords(CourseAttributes course, String studentEmail) {
+ WebElement studentRow = getStudentRow(course, studentEmail);
+ WebElement viewButton = studentRow.findElement(By.id("btn-view-records"));
+ click(viewButton);
+ ThreadHelper.waitFor(2000);
+ switchToNewWindow();
+ return changePageType(InstructorStudentRecordsPage.class);
+ }
+
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorStudentRecordsPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorStudentRecordsPage.java
new file mode 100644
index 00000000000..f6c18d9b1dc
--- /dev/null
+++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorStudentRecordsPage.java
@@ -0,0 +1,84 @@
+package teammates.e2e.pageobjects;
+
+import static org.junit.Assert.assertEquals;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.attributes.StudentAttributes;
+import teammates.common.datatransfer.attributes.StudentProfileAttributes;
+
+/**
+ * Page Object Model for instructor student records page.
+ */
+public class InstructorStudentRecordsPage extends AppPage {
+ private static final String NOT_SPECIFIED_LABEL = "Not Specified";
+
+ @FindBy(id = "records-header")
+ private WebElement headerText;
+
+ @FindBy (id = "name-with-gender")
+ private WebElement studentNameWithGender;
+
+ @FindBy (id = "personal-email")
+ private WebElement studentPersonalEmail;
+
+ @FindBy (id = "institution")
+ private WebElement studentInstitution;
+
+ @FindBy (id = "nationality")
+ private WebElement studentNationality;
+
+ @FindBy (id = "more-info")
+ private WebElement moreInformation;
+
+ public InstructorStudentRecordsPage(Browser browser) {
+ super(browser);
+ }
+
+ @Override
+ protected boolean containsExpectedPageContents() {
+ return getPageSource().contains("'s Records");
+ }
+
+ public void verifyIsCorrectPage(String courseId, String studentName) {
+ String expected = String.format("%s's Records - %s", studentName, courseId);
+ assertEquals(expected, headerText.getText());
+ }
+
+ public void verifyStudentDetails(StudentProfileAttributes studentProfile, StudentAttributes student) {
+ verifyIsCorrectPage(student.getCourse(), student.getName());
+
+ StudentProfileAttributes profileToTest = studentProfile;
+ if (studentProfile == null) {
+ profileToTest = StudentProfileAttributes.builder(student.getGoogleId()).build();
+ }
+ verifyDetail(getExpectedNameWithGender(profileToTest), studentNameWithGender);
+ verifyDetail(profileToTest.getEmail(), studentPersonalEmail);
+ verifyDetail(profileToTest.getInstitute(), studentInstitution);
+ verifyDetail(profileToTest.getNationality(), studentNationality);
+ verifyDetail(profileToTest.getMoreInfo(), moreInformation);
+ }
+
+ private void verifyDetail(String expected, WebElement detailField) {
+ if (expected.isEmpty()) {
+ assertEquals(NOT_SPECIFIED_LABEL, detailField.getText());
+ } else {
+ assertEquals(expected, detailField.getText());
+ }
+ }
+
+ private String getExpectedNameWithGender(StudentProfileAttributes profile) {
+ String name = profile.getShortName();
+ StudentProfileAttributes.Gender gender = profile.getGender();
+ String expectedName = name.isEmpty()
+ ? NOT_SPECIFIED_LABEL
+ : name;
+ String expectedGender = gender.equals(StudentProfileAttributes.Gender.OTHER)
+ ? NOT_SPECIFIED_LABEL
+ : gender.toString();
+
+ return expectedName + " (" + expectedGender + ")";
+ }
+
+}
diff --git a/src/e2e/java/teammates/e2e/pageobjects/StudentCourseDetailsPage.java b/src/e2e/java/teammates/e2e/pageobjects/StudentCourseDetailsPage.java
index ecfa960cb25..093705f9c09 100644
--- a/src/e2e/java/teammates/e2e/pageobjects/StudentCourseDetailsPage.java
+++ b/src/e2e/java/teammates/e2e/pageobjects/StudentCourseDetailsPage.java
@@ -1,19 +1,106 @@
package teammates.e2e.pageobjects;
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.datatransfer.attributes.InstructorAttributes;
+import teammates.common.datatransfer.attributes.StudentAttributes;
+import teammates.common.datatransfer.attributes.StudentProfileAttributes;
+import teammates.common.util.StringHelper;
/**
* Page Object Model for student course details page.
*/
public class StudentCourseDetailsPage extends AppPage {
+ @FindBy(id = "course-name")
+ private WebElement courseNameField;
+
+ @FindBy(id = "course-id")
+ private WebElement courseIdField;
+
+ @FindBy(id = "instructors")
+ private WebElement instructorsList;
+
+ @FindBy(id = "student-name")
+ private WebElement studentNameField;
+
+ @FindBy(id = "student-section")
+ private WebElement studentSectionField;
+
+ @FindBy(id = "student-team")
+ private WebElement studentTeamField;
+
+ @FindBy(id = "student-email")
+ private WebElement studentEmailField;
+
public StudentCourseDetailsPage(Browser browser) {
super(browser);
}
@Override
protected boolean containsExpectedPageContents() {
- return browser.driver.findElement(By.tagName("h4")).getText().equals("Course");
+ return waitForElementPresence(By.tagName("h1")).getText().matches("Team Details for .+");
+ }
+
+ public void verifyCourseDetails(CourseAttributes courseDetails) {
+ assertEquals(courseDetails.getName(), courseNameField.getText());
+ assertEquals(courseDetails.getId(), courseIdField.getText());
+ }
+
+ public void verifyInstructorsDetails(InstructorAttributes[] instructorDetails) {
+ String[] actualInstructors = instructorsList.getText().split(System.lineSeparator());
+ for (int i = 0; i < instructorDetails.length; i++) {
+ InstructorAttributes expected = instructorDetails[i];
+ assertEquals(expected.displayedName + ": " + expected.name + " (" + expected.email + ")",
+ actualInstructors[i]);
+ }
+ }
+
+ public void verifyStudentDetails(StudentAttributes studentDetails) {
+ assertEquals(studentDetails.getName(), studentNameField.getText());
+ assertEquals(studentDetails.getSection(), studentSectionField.getText());
+ assertEquals(studentDetails.getTeam(), studentTeamField.getText());
+ assertEquals(studentDetails.getEmail(), studentEmailField.getText());
+ }
+
+ private String convertGender(StudentProfileAttributes.Gender gender) {
+ switch (gender) {
+ case MALE:
+ return "Male";
+ case FEMALE:
+ return "Female";
+ default:
+ return "Not Specified";
+ }
+ }
+
+ public void verifyTeammatesDetails(StudentAttributes[] teammates, StudentProfileAttributes[] teammateProfiles) {
+ int numTables = teammateProfiles.length;
+
+ for (int i = 0; i < numTables; i++) {
+ List profileItems = new ArrayList<>();
+ profileItems.add("Name: " + teammates[i].getName());
+ profileItems.add("Email: " + teammates[i].getEmail());
+ profileItems.add("Gender: " + convertGender(teammateProfiles[i].getGender()));
+ if (!StringHelper.isEmpty(teammateProfiles[i].getInstitute())) {
+ profileItems.add("Institute: " + teammateProfiles[i].getInstitute());
+ }
+ if (!StringHelper.isEmpty(teammateProfiles[i].getNationality())) {
+ profileItems.add("Nationality: " + teammateProfiles[i].getNationality());
+ }
+ }
+ }
+
+ public void sortTeammatesByName() {
+ click(browser.driver.findElement(By.id("sort-name")));
}
}
diff --git a/src/e2e/java/teammates/e2e/util/BackDoor.java b/src/e2e/java/teammates/e2e/util/BackDoor.java
index e1d0d005656..477fabf8574 100644
--- a/src/e2e/java/teammates/e2e/util/BackDoor.java
+++ b/src/e2e/java/teammates/e2e/util/BackDoor.java
@@ -6,9 +6,11 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
+import java.time.Duration;
+import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -30,24 +32,41 @@
import org.apache.http.message.BasicNameValuePair;
import teammates.common.datatransfer.DataBundle;
+import teammates.common.datatransfer.FeedbackParticipantType;
+import teammates.common.datatransfer.attributes.AccountAttributes;
import teammates.common.datatransfer.attributes.CourseAttributes;
+import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes;
+import teammates.common.datatransfer.attributes.FeedbackResponseAttributes;
+import teammates.common.datatransfer.attributes.FeedbackResponseCommentAttributes;
+import teammates.common.datatransfer.attributes.FeedbackSessionAttributes;
import teammates.common.datatransfer.attributes.InstructorAttributes;
import teammates.common.datatransfer.attributes.StudentAttributes;
import teammates.common.datatransfer.attributes.StudentProfileAttributes;
import teammates.common.exception.HttpRequestFailedException;
+import teammates.common.util.Assumption;
import teammates.common.util.Const;
import teammates.common.util.JsonUtils;
-import teammates.ui.webapi.output.CourseData;
-import teammates.ui.webapi.output.CoursesData;
-import teammates.ui.webapi.output.InstructorData;
-import teammates.ui.webapi.output.InstructorsData;
-import teammates.ui.webapi.output.StudentData;
-import teammates.ui.webapi.request.Intent;
+import teammates.ui.output.AccountData;
+import teammates.ui.output.CourseData;
+import teammates.ui.output.CoursesData;
+import teammates.ui.output.FeedbackQuestionData;
+import teammates.ui.output.FeedbackQuestionsData;
+import teammates.ui.output.FeedbackResponseCommentData;
+import teammates.ui.output.FeedbackResponseData;
+import teammates.ui.output.FeedbackResponsesData;
+import teammates.ui.output.FeedbackSessionData;
+import teammates.ui.output.FeedbackSessionsData;
+import teammates.ui.output.FeedbackVisibilityType;
+import teammates.ui.output.InstructorData;
+import teammates.ui.output.InstructorsData;
+import teammates.ui.output.NumberOfEntitiesToGiveFeedbackToSetting;
+import teammates.ui.output.ResponseVisibleSetting;
+import teammates.ui.output.SessionVisibleSetting;
+import teammates.ui.output.StudentData;
+import teammates.ui.request.Intent;
/**
* Used to create API calls to the back-end without going through the UI.
- *
- * Note that this will replace {@link teammates.test.driver.BackDoor} once the front-end migration is complete.
*/
public final class BackDoor {
@@ -60,7 +79,7 @@ private BackDoor() {
*
* @return The body content and status of the HTTP response
*/
- public static ResponseBodyAndCode executeGetRequest(String relativeUrl, Map params) {
+ public static ResponseBodyAndCode executeGetRequest(String relativeUrl, Map params) {
return executeRequest(HttpGet.METHOD_NAME, relativeUrl, params, null);
}
@@ -69,7 +88,7 @@ public static ResponseBodyAndCode executeGetRequest(String relativeUrl, Map params, String body) {
+ public static ResponseBodyAndCode executePostRequest(String relativeUrl, Map params, String body) {
return executeRequest(HttpPost.METHOD_NAME, relativeUrl, params, body);
}
@@ -78,7 +97,7 @@ public static ResponseBodyAndCode executePostRequest(String relativeUrl, Map params, String body) {
+ public static ResponseBodyAndCode executePutRequest(String relativeUrl, Map params, String body) {
return executeRequest(HttpPut.METHOD_NAME, relativeUrl, params, body);
}
@@ -87,7 +106,7 @@ public static ResponseBodyAndCode executePutRequest(String relativeUrl, Map params) {
+ public static ResponseBodyAndCode executeDeleteRequest(String relativeUrl, Map params) {
return executeRequest(HttpDelete.METHOD_NAME, relativeUrl, params, null);
}
@@ -97,8 +116,8 @@ public static ResponseBodyAndCode executeDeleteRequest(String relativeUrl, Map params, String body) {
- String url = TestProperties.TEAMMATES_URL + Const.ResourceURIs.URI_PREFIX + relativeUrl;
+ String method, String relativeUrl, Map params, String body) {
+ String url = TestProperties.TEAMMATES_URL + relativeUrl;
HttpRequestBase request;
switch (method) {
@@ -142,11 +161,11 @@ private static ResponseBodyAndCode executeRequest(
*
* @return The content of the HTTP response
*/
- private static HttpGet createGetRequest(String url, Map params) {
+ private static HttpGet createGetRequest(String url, Map params) {
return new HttpGet(createBasicUri(url, params));
}
- private static HttpPost createPostRequest(String url, Map params, String body) {
+ private static HttpPost createPostRequest(String url, Map params, String body) {
HttpPost post = new HttpPost(createBasicUri(url, params));
if (body != null) {
@@ -157,7 +176,7 @@ private static HttpPost createPostRequest(String url, Map para
return post;
}
- private static HttpPut createPutRequest(String url, Map params, String body) {
+ private static HttpPut createPutRequest(String url, Map params, String body) {
HttpPut put = new HttpPut(createBasicUri(url, params));
if (body != null) {
@@ -168,16 +187,14 @@ private static HttpPut createPutRequest(String url, Map params
return put;
}
- private static HttpDelete createDeleteRequest(String url, Map params) {
+ private static HttpDelete createDeleteRequest(String url, Map params) {
return new HttpDelete(createBasicUri(url, params));
}
- private static URI createBasicUri(String url, Map params) {
+ private static URI createBasicUri(String url, Map params) {
List postParameters = new ArrayList<>();
if (params != null) {
- params.forEach((key, values) -> Arrays.stream(values).forEach(value -> {
- postParameters.add(new BasicNameValuePair(key, value));
- }));
+ params.forEach((key, value) -> postParameters.add(new BasicNameValuePair(key, value)));
}
try {
@@ -249,13 +266,33 @@ public static String putDocuments(DataBundle dataBundle) {
? Const.StatusCodes.BACKDOOR_STATUS_SUCCESS : Const.StatusCodes.BACKDOOR_STATUS_FAILURE;
}
+ /**
+ * Gets an account from the datastore.
+ */
+ public static AccountAttributes getAccount(String googleId) {
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.INSTRUCTOR_ID, googleId);
+ ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.ACCOUNT, params);
+ if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
+ return null;
+ }
+
+ AccountData accountData = JsonUtils.fromJson(response.responseBody, AccountData.class);
+ return AccountAttributes.builder(accountData.getGoogleId())
+ .withName(accountData.getName())
+ .withEmail(accountData.getEmail())
+ .withInstitute(accountData.getInstitute())
+ .withIsInstructor(accountData.isInstructor())
+ .build();
+ }
+
/**
* Gets a student's profile from the datastore.
*/
public static StudentProfileAttributes getStudentProfile(String courseId, String studentEmail) {
- Map params = new HashMap<>();
- params.put(Const.ParamsNames.COURSE_ID, new String[] { courseId });
- params.put(Const.ParamsNames.STUDENT_EMAIL, new String[] { studentEmail });
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
+ params.put(Const.ParamsNames.STUDENT_EMAIL, studentEmail);
ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.STUDENT_PROFILE, params);
if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
return null;
@@ -268,8 +305,9 @@ public static StudentProfileAttributes getStudentProfile(String courseId, String
* Gets course data from the datastore.
*/
public static CourseData getCourseData(String courseId) {
- Map params = new HashMap<>();
- params.put(Const.ParamsNames.COURSE_ID, new String[] { courseId });
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
+ params.put(Const.ParamsNames.ENTITY_TYPE, Const.EntityType.STUDENT);
ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.COURSE, params);
if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
return null;
@@ -296,24 +334,29 @@ public static CourseAttributes getCourse(String courseId) {
* Gets archived course data from the datastore.
*/
public static CourseData getArchivedCourseData(String instructorId, String courseId) {
- Map params = new HashMap<>();
- params.put(Const.ParamsNames.USER_ID, new String[] { instructorId });
- params.put(Const.ParamsNames.COURSE_ID, new String[] { courseId });
- params.put(Const.ParamsNames.ENTITY_TYPE, new String[] { Const.EntityType.INSTRUCTOR });
- params.put(Const.ParamsNames.COURSE_STATUS, new String[] { Const.CourseStatus.ARCHIVED });
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.USER_ID, instructorId);
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
+ params.put(Const.ParamsNames.ENTITY_TYPE, Const.EntityType.INSTRUCTOR);
+ params.put(Const.ParamsNames.COURSE_STATUS, Const.CourseStatus.ARCHIVED);
ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.COURSES, params);
if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
return null;
}
+
CoursesData coursesData = JsonUtils.fromJson(response.responseBody, CoursesData.class);
- List courseDataList = coursesData.getCourses();
- for (CourseData courseData : courseDataList) {
- if (courseData.getCourseId().equals(courseId)) {
- return courseData;
- }
+ CourseData courseData = coursesData.getCourses()
+ .stream()
+ .filter(cd -> cd.getCourseId().equals(courseId))
+ .findFirst()
+ .orElse(null);
+
+ if (courseData == null) {
+ return null;
}
- return null;
+
+ return courseData;
}
/**
@@ -334,22 +377,26 @@ public static CourseAttributes getArchivedCourse(String instructorId, String cou
* Gets instructor data from the datastore.
*/
public static InstructorData getInstructorData(String courseId, String email) {
- Map params = new HashMap<>();
- params.put(Const.ParamsNames.COURSE_ID, new String[] { courseId });
- params.put(Const.ParamsNames.INTENT, new String[] { Intent.FULL_DETAIL.toString() });
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
+ params.put(Const.ParamsNames.INTENT, Intent.FULL_DETAIL.toString());
ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.INSTRUCTORS, params);
if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
return null;
}
InstructorsData instructorsData = JsonUtils.fromJson(response.responseBody, InstructorsData.class);
- List instructorsDataList = instructorsData.getInstructors();
- for (InstructorData instructor : instructorsDataList) {
- if (instructor.getEmail().equals(email)) {
- return instructor;
- }
+ InstructorData instructorData = instructorsData.getInstructors()
+ .stream()
+ .filter(instructor -> instructor.getEmail().equals(email))
+ .findFirst()
+ .orElse(null);
+
+ if (instructorData == null) {
+ return null;
}
- return null;
+
+ return instructorData;
}
/**
@@ -377,16 +424,20 @@ public static InstructorAttributes getInstructor(String courseId, String instruc
if (instructorData.getDisplayedToStudentsAs() != null) {
instructor.withDisplayedName(instructorData.getDisplayedToStudentsAs());
}
- return instructor.build();
+ InstructorAttributes instructorAttributes = instructor.build();
+ if (instructorData.getKey() != null) {
+ instructorAttributes.key = instructorData.getKey();
+ }
+ return instructorAttributes;
}
/**
* Gets student data from the datastore.
*/
public static StudentData getStudentData(String courseId, String studentEmail) {
- Map params = new HashMap<>();
- params.put(Const.ParamsNames.COURSE_ID, new String[] { courseId });
- params.put(Const.ParamsNames.STUDENT_EMAIL, new String[] { studentEmail });
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
+ params.put(Const.ParamsNames.STUDENT_EMAIL, studentEmail);
ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.STUDENT, params);
if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
return null;
@@ -402,35 +453,258 @@ public static StudentAttributes getStudent(String courseId, String studentEmail)
if (studentData == null) {
return null;
}
- StudentAttributes.Builder student = StudentAttributes.builder(studentData.getCourseId(),
+ StudentAttributes.Builder builder = StudentAttributes.builder(studentData.getCourseId(),
studentData.getEmail());
if (studentData.getGoogleId() != null) {
- student.withGoogleId(studentData.getGoogleId());
+ builder.withGoogleId(studentData.getGoogleId());
}
if (studentData.getName() != null) {
- student.withName(studentData.getName());
+ builder.withName(studentData.getName());
}
if (studentData.getSectionName() != null) {
- student.withSectionName(studentData.getSectionName());
+ builder.withSectionName(studentData.getSectionName());
}
if (studentData.getTeamName() != null) {
- student.withTeamName(studentData.getTeamName());
+ builder.withTeamName(studentData.getTeamName());
}
if (studentData.getComments() != null) {
- student.withComment(studentData.getComments());
+ builder.withComment(studentData.getComments());
}
if (studentData.getLastName() != null) {
- student.withLastName(studentData.getLastName());
+ builder.withLastName(studentData.getLastName());
+ }
+ StudentAttributes student = builder.build();
+ if (studentData.getKey() != null) {
+ student.key = studentData.getKey();
}
- return student.build();
+ return student;
+ }
+
+ /**
+ * Get feedback session data from datastore.
+ */
+ public static FeedbackSessionData getFeedbackSessionData(String courseId, String feedbackSessionName) {
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
+ params.put(Const.ParamsNames.FEEDBACK_SESSION_NAME, feedbackSessionName);
+ params.put(Const.ParamsNames.INTENT, Intent.FULL_DETAIL.toString());
+ ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.SESSION, params);
+ if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
+ return null;
+ }
+ return JsonUtils.fromJson(response.responseBody, FeedbackSessionData.class);
+ }
+
+ /**
+ * Get feedback session from datastore.
+ */
+ public static FeedbackSessionAttributes getFeedbackSession(String courseId, String feedbackSessionName) {
+ FeedbackSessionData sessionData = getFeedbackSessionData(courseId, feedbackSessionName);
+ if (sessionData == null) {
+ return null;
+ }
+
+ FeedbackSessionAttributes sessionAttributes = FeedbackSessionAttributes
+ .builder(sessionData.getFeedbackSessionName(), sessionData.getCourseId())
+ .withInstructions(sessionData.getInstructions())
+ .withStartTime(Instant.ofEpochMilli(sessionData.getSubmissionStartTimestamp()))
+ .withEndTime(Instant.ofEpochMilli(sessionData.getSubmissionEndTimestamp()))
+ .withTimeZone(ZoneId.of(sessionData.getTimeZone()))
+ .withGracePeriod(Duration.ofMinutes(sessionData.getGracePeriod()))
+ .withIsClosingEmailEnabled(sessionData.getIsClosingEmailEnabled())
+ .withIsPublishedEmailEnabled(sessionData.getIsPublishedEmailEnabled())
+ .build();
+
+ sessionAttributes.setCreatedTime(Instant.ofEpochMilli(sessionData.getCreatedAtTimestamp()));
+
+ if (sessionData.getSessionVisibleSetting().equals(SessionVisibleSetting.AT_OPEN)) {
+ sessionAttributes.setSessionVisibleFromTime(Const.TIME_REPRESENTS_FOLLOW_OPENING);
+ } else {
+ sessionAttributes.setSessionVisibleFromTime(Instant.ofEpochMilli(
+ sessionData.getCustomSessionVisibleTimestamp()));
+ }
+
+ if (sessionData.getResponseVisibleSetting().equals(ResponseVisibleSetting.AT_VISIBLE)) {
+ sessionAttributes.setResultsVisibleFromTime(Const.TIME_REPRESENTS_FOLLOW_VISIBLE);
+ } else if (sessionData.getResponseVisibleSetting().equals(ResponseVisibleSetting.LATER)) {
+ sessionAttributes.setResultsVisibleFromTime(Const.TIME_REPRESENTS_LATER);
+ } else {
+ sessionAttributes.setResultsVisibleFromTime(Instant.ofEpochMilli(
+ sessionData.getCustomResponseVisibleTimestamp()));
+ }
+
+ return sessionAttributes;
+ }
+
+ /**
+ * Get soft deleted feedback session from datastore.
+ */
+ public static FeedbackSessionAttributes getSoftDeletedSession(String feedbackSessionName, String instructorId) {
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.ENTITY_TYPE, Const.EntityType.INSTRUCTOR);
+ params.put(Const.ParamsNames.IS_IN_RECYCLE_BIN, "true");
+ params.put(Const.ParamsNames.USER_ID, instructorId);
+ ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.SESSIONS, params);
+ if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
+ return null;
+ }
+
+ FeedbackSessionsData sessionsData = JsonUtils.fromJson(response.responseBody, FeedbackSessionsData.class);
+ FeedbackSessionData feedbackSession = sessionsData.getFeedbackSessions()
+ .stream()
+ .filter(fs -> fs.getFeedbackSessionName().equals(feedbackSessionName))
+ .findFirst()
+ .orElse(null);
+
+ if (feedbackSession == null) {
+ return null;
+ }
+
+ return FeedbackSessionAttributes
+ .builder(feedbackSession.getCourseId(), feedbackSession.getFeedbackSessionName())
+ .build();
+ }
+
+ /**
+ * Get feedback question from datastore.
+ */
+ public static FeedbackQuestionAttributes getFeedbackQuestion(String courseId, String feedbackSessionName,
+ int qnNumber) {
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
+ params.put(Const.ParamsNames.FEEDBACK_SESSION_NAME, feedbackSessionName);
+ params.put(Const.ParamsNames.INTENT, Intent.FULL_DETAIL.toString());
+ ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.QUESTIONS, params);
+ if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
+ return null;
+ }
+
+ FeedbackQuestionsData questionsData = JsonUtils.fromJson(response.responseBody, FeedbackQuestionsData.class);
+ FeedbackQuestionData question = questionsData.getQuestions()
+ .stream()
+ .filter(fq -> fq.getQuestionNumber() == qnNumber)
+ .findFirst()
+ .orElse(null);
+
+ if (question == null) {
+ return null;
+ }
+
+ FeedbackQuestionAttributes questionAttr = FeedbackQuestionAttributes.builder()
+ .withCourseId(courseId)
+ .withFeedbackSessionName(feedbackSessionName)
+ .withQuestionDetails(question.getQuestionDetails())
+ .withQuestionDescription(question.getQuestionDescription())
+ .withQuestionNumber(question.getQuestionNumber())
+ .withGiverType(question.getGiverType())
+ .withRecipientType(question.getRecipientType())
+ .withNumberOfEntitiesToGiveFeedbackTo(question.getNumberOfEntitiesToGiveFeedbackToSetting()
+ .equals(NumberOfEntitiesToGiveFeedbackToSetting.UNLIMITED)
+ ? Const.MAX_POSSIBLE_RECIPIENTS
+ : question.getCustomNumberOfEntitiesToGiveFeedbackTo())
+ .withShowResponsesTo(convertToFeedbackParticipantType(question.getShowResponsesTo()))
+ .withShowGiverNameTo(convertToFeedbackParticipantType(question.getShowGiverNameTo()))
+ .withShowRecipientNameTo(convertToFeedbackParticipantType(question.getShowRecipientNameTo()))
+ .build();
+ if (question.getFeedbackQuestionId() != null) {
+ questionAttr.setId(question.getFeedbackQuestionId());
+ }
+ return questionAttr;
+ }
+
+ /**
+ * Converts List of FeedbackParticipantType to sorted List of FeedbackVisibilityType.
+ */
+ private static List convertToFeedbackParticipantType(
+ List feedbackVisibilityTypeList) {
+ List feedbackParticipantTypeList = feedbackVisibilityTypeList.stream()
+ .map(feedbackParticipantType -> {
+ switch (feedbackParticipantType) {
+ case STUDENTS:
+ return FeedbackParticipantType.STUDENTS;
+ case INSTRUCTORS:
+ return FeedbackParticipantType.INSTRUCTORS;
+ case RECIPIENT:
+ return FeedbackParticipantType.RECEIVER;
+ case GIVER_TEAM_MEMBERS:
+ return FeedbackParticipantType.OWN_TEAM_MEMBERS;
+ case RECIPIENT_TEAM_MEMBERS:
+ return FeedbackParticipantType.RECEIVER_TEAM_MEMBERS;
+ default:
+ Assumption.fail("Unknown FeedbackVisibilityType " + feedbackParticipantType);
+ break;
+ }
+ return null;
+ }).collect(Collectors.toList());
+ Collections.sort(feedbackParticipantTypeList);
+ return feedbackParticipantTypeList;
+ }
+
+ /**
+ * Get feedback response from datastore.
+ */
+ public static FeedbackResponseAttributes getFeedbackResponse(String feedbackQuestionId, String giver,
+ String recipient) {
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.FEEDBACK_QUESTION_ID, feedbackQuestionId);
+ params.put(Const.ParamsNames.INTENT, Intent.STUDENT_SUBMISSION.toString());
+ params.put(Const.ParamsNames.FEEDBACK_SESSION_MODERATED_PERSON, giver);
+ ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.RESPONSES, params);
+ if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
+ return null;
+ }
+
+ FeedbackResponsesData responsesData = JsonUtils.fromJson(response.responseBody, FeedbackResponsesData.class);
+ FeedbackResponseData fr = responsesData.getResponses()
+ .stream()
+ .filter(r -> r.getGiverIdentifier().equals(giver) && r.getRecipientIdentifier().equals(recipient))
+ .findFirst()
+ .orElse(null);
+
+ if (fr == null) {
+ return null;
+ }
+
+ FeedbackResponseAttributes responseAttr = FeedbackResponseAttributes
+ .builder(feedbackQuestionId, fr.getGiverIdentifier(), fr.getRecipientIdentifier())
+ .withResponseDetails(fr.getResponseDetails())
+ .build();
+ if (fr.getFeedbackResponseId() != null) {
+ responseAttr.setId(fr.getFeedbackResponseId());
+ }
+ return responseAttr;
+ }
+
+ /**
+ * Get feedback response comment from datastore.
+ */
+ public static FeedbackResponseCommentAttributes getFeedbackResponseComment(String feedbackResponseId) {
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.FEEDBACK_RESPONSE_ID, feedbackResponseId);
+ params.put(Const.ParamsNames.INTENT, Intent.STUDENT_SUBMISSION.toString());
+ ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.RESPONSE_COMMENT, params);
+ if (response.responseCode == HttpStatus.SC_NOT_FOUND) {
+ return null;
+ }
+
+ FeedbackResponseCommentData frc = JsonUtils.fromJson(response.responseBody, FeedbackResponseCommentData.class);
+
+ if (frc == null) {
+ return null;
+ }
+
+ return FeedbackResponseCommentAttributes.builder()
+ .withCommentGiver(frc.getCommentGiver())
+ .withCommentText(frc.getCommentText())
+ .build();
}
/**
* Deletes a student from the datastore.
*/
public static void deleteStudent(String unregUserId) {
- Map params = new HashMap<>();
- params.put(Const.ParamsNames.STUDENT_ID, new String[] { unregUserId });
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.STUDENT_ID, unregUserId);
executeDeleteRequest(Const.ResourceURIs.STUDENTS, params);
}
@@ -438,9 +712,9 @@ public static void deleteStudent(String unregUserId) {
* Deletes a feedback session from the datastore.
*/
public static void deleteFeedbackSession(String feedbackSession, String courseId) {
- Map params = new HashMap<>();
- params.put(Const.ParamsNames.FEEDBACK_SESSION_NAME, new String[] { feedbackSession });
- params.put(Const.ParamsNames.COURSE_ID, new String[] { courseId });
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.FEEDBACK_SESSION_NAME, feedbackSession);
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
executeDeleteRequest(Const.ResourceURIs.SESSION, params);
}
@@ -448,8 +722,8 @@ public static void deleteFeedbackSession(String feedbackSession, String courseId
* Deletes a course from the datastore.
*/
public static void deleteCourse(String courseId) {
- Map params = new HashMap<>();
- params.put(Const.ParamsNames.COURSE_ID, new String[] { courseId });
+ Map params = new HashMap<>();
+ params.put(Const.ParamsNames.COURSE_ID, courseId);
executeDeleteRequest(Const.ResourceURIs.COURSE, params);
}
diff --git a/src/e2e/java/teammates/e2e/util/EmailAccount.java b/src/e2e/java/teammates/e2e/util/EmailAccount.java
index f08fbbaee3b..58808268787 100644
--- a/src/e2e/java/teammates/e2e/util/EmailAccount.java
+++ b/src/e2e/java/teammates/e2e/util/EmailAccount.java
@@ -99,6 +99,27 @@ public String getRegistrationKeyFromUnreadEmails(String courseName, String cours
return null;
}
+ /**
+ * Returns true if unread mail contains mail with the specified subject.
+ */
+ public boolean isEmailWithSubjectPresent(String subject)
+ throws IOException, MessagingException {
+
+ List messageStubs = getListOfUnreadEmailOfUser();
+
+ for (Message messageStub : messageStubs) {
+ Message message = service.users().messages().get(username, messageStub.getId()).setFormat("raw")
+ .execute();
+
+ MimeMessage email = convertFromMessageToMimeMessage(message);
+
+ if (email.getSubject().equals(subject)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Marks all unread emails in the user's inbox as read.
*/
diff --git a/src/e2e/java/teammates/e2e/util/PriorityInterceptor.java b/src/e2e/java/teammates/e2e/util/PriorityInterceptor.java
index 422316ab0f1..b70bf92c991 100644
--- a/src/e2e/java/teammates/e2e/util/PriorityInterceptor.java
+++ b/src/e2e/java/teammates/e2e/util/PriorityInterceptor.java
@@ -9,7 +9,7 @@
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;
-import teammates.test.driver.FileHelper;
+import teammates.test.FileHelper;
/**
* Allows TestNG to run tests in a specified order, based on the {@code Priority(n)} annotation.
diff --git a/src/e2e/java/teammates/e2e/util/TestProperties.java b/src/e2e/java/teammates/e2e/util/TestProperties.java
index dda59164a06..2e28b832d5b 100644
--- a/src/e2e/java/teammates/e2e/util/TestProperties.java
+++ b/src/e2e/java/teammates/e2e/util/TestProperties.java
@@ -6,7 +6,7 @@
import java.nio.file.Paths;
import java.util.Properties;
-import teammates.common.util.Url;
+import teammates.common.util.UrlExtension;
/**
* Represents properties in test.properties file.
@@ -25,12 +25,12 @@ public final class TestProperties {
/** The directory where HTML files for testing pages are stored. */
public static final String TEST_PAGES_FOLDER = "src/e2e/resources/pages";
- /** The directory where HTML files for testing email contents are stored. */
- public static final String TEST_EMAILS_FOLDER = "src/e2e/resources/emails";
-
/** The directory where JSON files used to create data bundles are stored. */
public static final String TEST_DATA_FOLDER = "src/e2e/resources/data";
+ /** The directory where webdriver downloads files to. */
+ public static final String TEST_DOWNLOADS_FOLDER = "src/e2e/resources/downloads";
+
/** The value of "test.app.url" in test.properties file. */
public static final String TEAMMATES_URL;
@@ -80,6 +80,9 @@ public final class TestProperties {
/** One of the allowed values of "test.selenium.browser" in test.properties file. */
public static final String BROWSER_FIREFOX = "firefox";
+ /** The value of "test.browser.closeonfailure" in test.properties file. */
+ public static final boolean CLOSE_BROWSER_ON_FAILURE;
+
/** The value of "test.firefox.path" in test.properties file. */
public static final String FIREFOX_PATH;
@@ -89,6 +92,12 @@ public final class TestProperties {
/** The value of "test.geckodriver.path" in test.properties file. */
public static final String GECKODRIVER_PATH;
+ /** The value of "test.firefox.profile.name" in test.properties file. */
+ public static final String FIREFOX_PROFILE_NAME;
+
+ /** The value of "test.chrome.userdata.path" in test.properties file. */
+ public static final String CHROME_USER_DATA_PATH;
+
/** The value of "test.timeout" in test.properties file. */
public static final int TEST_TIMEOUT;
@@ -111,7 +120,7 @@ public final class TestProperties {
prop.load(testPropStream);
}
- TEAMMATES_URL = Url.trimTrailingSlash(prop.getProperty("test.app.url"));
+ TEAMMATES_URL = UrlExtension.trimTrailingSlash(prop.getProperty("test.app.url"));
Properties buildProperties = new Properties();
try (InputStream buildPropStream = Files.newInputStream(Paths.get("src/main/resources/build.properties"))) {
@@ -138,9 +147,12 @@ public final class TestProperties {
BACKDOOR_KEY = prop.getProperty("test.backdoor.key");
BROWSER = prop.getProperty("test.selenium.browser").toLowerCase();
+ CLOSE_BROWSER_ON_FAILURE = Boolean.parseBoolean(prop.getProperty("test.browser.closeonfailure"));
FIREFOX_PATH = prop.getProperty("test.firefox.path");
CHROMEDRIVER_PATH = prop.getProperty("test.chromedriver.path");
GECKODRIVER_PATH = prop.getProperty("test.geckodriver.path");
+ FIREFOX_PROFILE_NAME = prop.getProperty("test.firefox.profile.name");
+ CHROME_USER_DATA_PATH = prop.getProperty("test.chrome.userdata.path");
TEST_TIMEOUT = Integer.parseInt(prop.getProperty("test.timeout"));
PERSISTENCE_RETRY_PERIOD_IN_S = Integer.parseInt(prop.getProperty("test.persistence.timeout"));
diff --git a/src/test/resources/data/AdminAccountDetailsPageUiTest.json b/src/e2e/resources/data/AdminAccountsPageE2ETest.json
similarity index 50%
rename from src/test/resources/data/AdminAccountDetailsPageUiTest.json
rename to src/e2e/resources/data/AdminAccountsPageE2ETest.json
index c0d64535e8c..280bbd476f5 100644
--- a/src/test/resources/data/AdminAccountDetailsPageUiTest.json
+++ b/src/e2e/resources/data/AdminAccountsPageE2ETest.json
@@ -1,90 +1,95 @@
{
"accounts": {
- "AAMgtUiT.instr2": {
- "googleId": "AAMgtUiT.instr2",
+ "AAMgtE2eT.instr2": {
+ "googleId": "AAMgtE2eT.instr2",
"name": "Teammates Instr2",
"isInstructor": true,
- "email": "AAMgtUiT.instr2@gmail.tmt",
+ "email": "AAMgtE2eT.instr2@gmail.tmt",
"institute": "TEAMMATES Test Institute 1"
}
},
"courses": {
- "AAMgtUiT.CS2103": {
- "id": "AAMgtUiT.CS2103",
+ "AAMgtE2eT.CS2104": {
+ "id": "AAMgtE2eT.CS2104",
+ "name": "Programming Languages",
+ "timeZone": "UTC"
+ },
+ "AAMgtE2eT.CS2103": {
+ "id": "AAMgtE2eT.CS2103",
"name": "Software Engineering",
"timeZone": "UTC"
},
- "AAMgtUiT.CS1101": {
- "id": "AAMgtUiT.CS1101",
+ "AAMgtE2eT.CS1101": {
+ "id": "AAMgtE2eT.CS1101",
"name": "Programming Methodology",
"timeZone": "UTC"
}
},
"instructors": {
- "AAMgtUiT.instr2-AAMgtUiT.CS2104": {
- "googleId": "AAMgtUiT.instr2",
- "courseId": "AAMgtUiT.CS2104",
+ "AAMgtE2eT.instr2-AAMgtE2eT.CS2104": {
+ "googleId": "AAMgtE2eT.instr2",
+ "courseId": "AAMgtE2eT.CS2104",
"name": "Teammates Instr2",
- "email": "AAMgtUiT.instr2@gmail.tmt",
+ "email": "AAMgtE2eT.instr2@gmail.tmt",
"role": "Co-owner",
"isDisplayedToStudents": false,
"displayedName": "Co-owner",
"privileges": {
"courseLevel": {
- "canviewstudentinsection": true,
- "cansubmitsessioninsection": true,
- "canmodifysessioncommentinsection": true,
"canmodifycourse": true,
- "canviewsessioninsection": true,
+ "canmodifyinstructor": true,
"canmodifysession": true,
"canmodifystudent": true,
- "canmodifyinstructor": true
+ "canviewstudentinsection": true,
+ "canviewsessioninsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true
},
"sectionLevel": {},
"sessionLevel": {}
}
},
- "AAMgtUiT.instr2-AAMgtUiT.CS2103": {
- "googleId": "AAMgtUiT.instr2",
- "courseId": "AAMgtUiT.CS2103",
+ "AAMgtE2eT.instr2-AAMgtE2eT.CS2103": {
+ "googleId": "AAMgtE2eT.instr2",
+ "courseId": "AAMgtE2eT.CS2103",
"name": "Teammates Instr2",
- "email": "AAMgtUiT.instr2@gmail.tmt",
+ "email": "AAMgtE2eT.instr2@gmail.tmt",
"role": "Co-owner",
"isDisplayedToStudents": false,
"displayedName": "Co-owner",
"privileges": {
"courseLevel": {
- "canviewstudentinsection": true,
- "cansubmitsessioninsection": true,
- "canmodifysessioncommentinsection": true,
"canmodifycourse": true,
- "canviewsessioninsection": true,
+ "canmodifyinstructor": true,
"canmodifysession": true,
"canmodifystudent": true,
- "canmodifyinstructor": true
+ "canviewstudentinsection": true,
+ "canviewsessioninsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true
},
"sectionLevel": {},
"sessionLevel": {}
}
},
- "AAMgtUiT.instr2-AAMgtUiT.CS1101": {
- "googleId": "AAMgtUiT.instr2",
- "courseId": "AAMgtUiT.CS1101",
+ "AAMgtE2eT.instr2-AAMgtE2eT.CS1101": {
+ "googleId": "AAMgtE2eT.instr2",
+ "courseId": "AAMgtE2eT.CS1101",
"name": "Teammates Instr2",
- "email": "AAMgtUiT.instr2@gmail.tmt",
+ "email": "AAMgtE2eT.instr2@gmail.tmt",
"role": "Co-owner",
"isDisplayedToStudents": false,
"displayedName": "Co-owner",
"privileges": {
"courseLevel": {
- "canviewstudentinsection": true,
- "cansubmitsessioninsection": true,
- "canmodifysessioncommentinsection": true,
"canmodifycourse": true,
- "canviewsessioninsection": true,
+ "canmodifyinstructor": true,
"canmodifysession": true,
"canmodifystudent": true,
- "canmodifyinstructor": true
+ "canviewstudentinsection": true,
+ "canviewsessioninsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true
},
"sectionLevel": {},
"sessionLevel": {}
@@ -92,13 +97,31 @@
}
},
"students": {
- "AAMgtUiT.instr2-student": {
- "googleId": "AAMgtUiT.instr2",
- "email": "AAMgtUiT.instr2@gmail.tmt",
- "course": "AAMgtUiT.CS1101",
- "name": "Teammates Instr2'\"",
- "comments": "This is an instructor'\"",
- "team": "Team 1'\"",
+ "AAMgtE2eT.instr2-student-CS2104": {
+ "googleId": "AAMgtE2eT.instr2",
+ "email": "AAMgtE2eT.instr2@gmail.tmt",
+ "course": "AAMgtE2eT.CS2104",
+ "name": "Teammates Instr2",
+ "comments": "This is an instructor",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "AAMgtE2eT.instr2-student-CS2103": {
+ "googleId": "AAMgtE2eT.instr2",
+ "email": "AAMgtE2eT.instr2@gmail.tmt",
+ "course": "AAMgtE2eT.CS2103",
+ "name": "Teammates Instr2",
+ "comments": "This is an instructor",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "AAMgtE2eT.instr2-student-CS1101": {
+ "googleId": "AAMgtE2eT.instr2",
+ "email": "AAMgtE2eT.instr2@gmail.tmt",
+ "course": "AAMgtE2eT.CS1101",
+ "name": "Teammates Instr2",
+ "comments": "This is an instructor",
+ "team": "Team 1",
"section": "None"
}
},
diff --git a/src/e2e/resources/data/AdminSessionsPageE2ETest.json b/src/e2e/resources/data/AdminSessionsPageE2ETest.json
new file mode 100644
index 00000000000..4fd5aed4e0b
--- /dev/null
+++ b/src/e2e/resources/data/AdminSessionsPageE2ETest.json
@@ -0,0 +1,105 @@
+{
+ "accounts": {
+ "instructor1OfCourse1": {
+ "googleId": "adminSessions.idOfInstructor1OfCourse1",
+ "name": "Instructor1 of Course1",
+ "isInstructor": true,
+ "email": "adminSessions.instructor1@course1.tmt",
+ "institute": "TEAMMATES Institute 1"
+ }
+ },
+ "courses": {
+ "typicalCourse1": {
+ "id": "adminSessions.idOfTypicalCourse1",
+ "name": "Typical Course 1",
+ "timeZone": "UTC"
+ }
+ },
+ "instructors": {
+ "instructor1OfCourse1": {
+ "googleId": "adminSessions.idOfInstructor1OfCourse1",
+ "courseId": "adminSessions.idOfTypicalCourse1",
+ "name": "Instructor1 of Course1",
+ "email": "adminSessions.instructor1@course1.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "feedbackSessions": {
+ "session1InCourse1": {
+ "feedbackSessionName": "Open feedback session",
+ "courseId": "adminSessions.idOfTypicalCourse1",
+ "creatorEmail": "adminSessions.instructor1@course1.tmt",
+ "instructions": "Please fill in the following questions.",
+ "createdTime": "2012-03-20T23:59:00Z",
+ "startTime": "2012-03-28T21:59:00Z",
+ "endTime": "2012-05-01T21:59:00Z",
+ "sessionVisibleFromTime": "2012-03-28T21:59:00Z",
+ "resultsVisibleFromTime": "2012-05-01T21:59:00Z",
+ "timeZone": "Asia/Singapore",
+ "gracePeriod": 10,
+ "sentOpenEmail": true,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "session2InCourse1": {
+ "feedbackSessionName": "Awaiting feedback session",
+ "courseId": "adminSessions.idOfTypicalCourse1",
+ "creatorEmail": "adminSessions.instructor1@course1.tmt",
+ "instructions": "Please fill in the following questions.",
+ "createdTime": "2012-03-20T23:59:00Z",
+ "startTime": "2012-03-28T21:59:00Z",
+ "endTime": "2012-05-01T21:59:00Z",
+ "sessionVisibleFromTime": "2012-03-28T21:59:00Z",
+ "resultsVisibleFromTime": "2012-05-01T21:59:00Z",
+ "timeZone": "Asia/Singapore",
+ "gracePeriod": 10,
+ "sentOpenEmail": true,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "session3InCourse1": {
+ "feedbackSessionName": "Future feedback session",
+ "courseId": "adminSessions.idOfTypicalCourse1",
+ "creatorEmail": "adminSessions.instructor1@course1.tmt",
+ "instructions": "Please fill in the following questions.",
+ "createdTime": "2012-03-20T23:59:00Z",
+ "startTime": "2012-03-28T21:59:00Z",
+ "endTime": "2012-05-01T21:59:00Z",
+ "sessionVisibleFromTime": "2012-03-28T21:59:00Z",
+ "resultsVisibleFromTime": "2012-05-01T21:59:00Z",
+ "timeZone": "Asia/Singapore",
+ "gracePeriod": 10,
+ "sentOpenEmail": true,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ }
+}
diff --git a/src/test/resources/data/AutomatedSessionRemindersTest.json b/src/e2e/resources/data/AutomatedSessionRemindersTest.json
similarity index 100%
rename from src/test/resources/data/AutomatedSessionRemindersTest.json
rename to src/e2e/resources/data/AutomatedSessionRemindersTest.json
diff --git a/src/e2e/resources/data/FeedbackConstSumOptionQuestionE2ETest.json b/src/e2e/resources/data/FeedbackConstSumOptionQuestionE2ETest.json
new file mode 100644
index 00000000000..1f6f7c7d8c9
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackConstSumOptionQuestionE2ETest.json
@@ -0,0 +1,222 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FConstSumOptionQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FConstSumOptionQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FConstSumOptionQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FConstSumOptionQuestionE2eT.instructor",
+ "courseId": "FConstSumOptionQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FConstSumOptionQuestionE2eT.instructor",
+ "courseId": "FConstSumOptionQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FConstSumOptionQuestionE2eT.CS2104": {
+ "googleId": "FConstSumOptionQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FConstSumOptionQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@FConstSumOptionQuestionE2eT.CS2104": {
+ "googleId": "FConstSumOptionQuestionE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "FConstSumOptionQuestionE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "charlie.tmms@FConstSumOptionQuestionE2eT.CS2104": {
+ "googleId": "charlie.tmms",
+ "email": "FConstSumOptionQuestionE2eT.charlie.tmms@gmail.tmt",
+ "course": "FConstSumOptionQuestionE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "danny.tmms@FConstSumOptionQuestionE2eT.CS1101": {
+ "googleId": "danny.tmms",
+ "email": "FConstSumOptionQuestionE2eT.danny.tmms@gmail.tmt",
+ "course": "FConstSumOptionQuestionE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FConstSumOptionQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FConstSumOptionQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FConstSumOptionQuestionE2eT.CS2104",
+ "questionDetails": {
+ "distributeToRecipients": false,
+ "forceUnevenDistribution": true,
+ "distributePointsFor": "All options",
+ "pointsPerOption": false,
+ "questionText": "How well did you do in each area? Give points accordingly.",
+ "numOfConstSumOptions": 3,
+ "questionType": "CONSTSUM_OPTIONS",
+ "points": 100,
+ "constSumOptions": [
+ "Teamwork",
+ "Creativity",
+ "Leadership"
+ ]
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "SELF",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FConstSumOptionQuestionE2eT.CS1101",
+ "questionDetails": {
+ "distributeToRecipients": false,
+ "forceUnevenDistribution": false,
+ "distributePointsFor": "None",
+ "pointsPerOption": true,
+ "questionText": "How much did you contribute to this area? Give points accordingly.",
+ "numOfConstSumOptions": 3,
+ "questionType": "CONSTSUM_OPTIONS",
+ "points": 20,
+ "constSumOptions": [
+ "Algo",
+ "UI",
+ "Testing"
+ ]
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "SELF",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/FeedbackConstSumRecipientQuestionE2ETest.json b/src/e2e/resources/data/FeedbackConstSumRecipientQuestionE2ETest.json
new file mode 100644
index 00000000000..dd7630848b0
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackConstSumRecipientQuestionE2ETest.json
@@ -0,0 +1,218 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FConstSumRecipientQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FConstSumRecipientQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FConstSumRecipientQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FConstSumRecipientQuestionE2eT.instructor",
+ "courseId": "FConstSumRecipientQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FConstSumRecipientQuestionE2eT.instructor",
+ "courseId": "FConstSumRecipientQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FConstSumRecipientQuestionE2eT.CS2104": {
+ "googleId": "FConstSumRecipientQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FConstSumRecipientQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@FConstSumRecipientQuestionE2eT.CS2104": {
+ "googleId": "FConstSumRecipientQuestionE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "FConstSumRecipientQuestionE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "charlie.tmms@FConstSumRecipientQuestionE2eT.CS2104": {
+ "googleId": "charlie.tmms",
+ "email": "FConstSumRecipientQuestionE2eT.charlie.tmms@gmail.tmt",
+ "course": "FConstSumRecipientQuestionE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 3",
+ "section": "None"
+ },
+ "danny.tmms@FConstSumRecipientQuestionE2eT.CS2104": {
+ "googleId": "danny.tmms",
+ "email": "FConstSumRecipientQuestionE2eT.danny.tmms@gmail.tmt",
+ "course": "FConstSumRecipientQuestionE2eT.CS2104",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 3",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FConstSumRecipientQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FConstSumRecipientQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FConstSumRecipientQuestionE2eT.CS2104",
+ "questionDetails": {
+ "distributeToRecipients": true,
+ "pointsPerOption": false,
+ "questionText": "Split points among the teams, according to how well you think each team did.",
+ "numOfConstSumOptions": 0,
+ "questionType": "CONSTSUM_RECIPIENTS",
+ "points": 100,
+ "forceUnevenDistribution": true,
+ "distributePointsFor": "All options",
+ "constSumOptions": []
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FConstSumRecipientQuestionE2eT.CS1101",
+ "questionDetails": {
+ "numOfConstSumOptions": 0,
+ "constSumOptions": [],
+ "distributeToRecipients": true,
+ "pointsPerOption": true,
+ "forceUnevenDistribution": false,
+ "distributePointsFor": "None",
+ "points": 20,
+ "questionType": "CONSTSUM_RECIPIENTS",
+ "questionText": "Split points among the teams, according to how creative you think each team was."
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/FeedbackContributionQuestionE2ETest.json b/src/e2e/resources/data/FeedbackContributionQuestionE2ETest.json
new file mode 100644
index 00000000000..4d9b66781e2
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackContributionQuestionE2ETest.json
@@ -0,0 +1,205 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FContributionQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FContributionQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FContributionQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FContributionQuestionE2eT.instructor",
+ "courseId": "FContributionQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FContributionQuestionE2eT.instructor",
+ "courseId": "FContributionQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FContributionQuestionE2eT.CS2104": {
+ "googleId": "FContributionQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FContributionQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@FContributionQuestionE2eT.CS2104": {
+ "googleId": "FContributionQuestionE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "FContributionQuestionE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "charlie.tmms@FContributionQuestionE2eT.CS2104": {
+ "googleId": "charlie.tmms",
+ "email": "FContributionQuestionE2eT.charlie.tmms@gmail.tmt",
+ "course": "FContributionQuestionE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "danny.tmms@FContributionQuestionE2eT.CS1101": {
+ "googleId": "danny.tmms",
+ "email": "FContributionQuestionE2eT.danny.tmms@gmail.tmt",
+ "course": "FContributionQuestionE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FContributionQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FContributionQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FContributionQuestionE2eT.CS2104",
+ "questionDetails": {
+ "isNotSureAllowed": true,
+ "questionType": "CONTRIB",
+ "questionText": "How much has each team member including yourself, contributed to the project?"
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "OWN_TEAM_MEMBERS_INCLUDING_SELF",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FContributionQuestionE2eT.CS1101",
+ "questionDetails": {
+ "isNotSureAllowed": false,
+ "questionType": "CONTRIB",
+ "questionText": "How much has each team member including yourself, contributed to the project?"
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "OWN_TEAM_MEMBERS_INCLUDING_SELF",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/FeedbackMcqQuestionE2ETest.json b/src/e2e/resources/data/FeedbackMcqQuestionE2ETest.json
new file mode 100644
index 00000000000..4dbfd3bf0eb
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackMcqQuestionE2ETest.json
@@ -0,0 +1,191 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FMcqQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FMcqQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FMcqQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FMcqQuestionE2eT.instructor",
+ "courseId": "FMcqQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FMcqQuestionE2eT.instructor",
+ "courseId": "FMcqQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FMcqQuestionE2eT.CS2104": {
+ "googleId": "FMcqQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FMcqQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FMcqQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FMcqQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FMcqQuestionE2eT.CS2104",
+ "questionDetails": {
+ "questionType": "MCQ",
+ "questionText": "Which area did you work on the most?",
+ "numOfMcqChoices": 3,
+ "mcqChoices": [
+ "UI",
+ "Algo",
+ "Testing"
+ ],
+ "otherEnabled": true,
+ "hasAssignedWeights": true,
+ "mcqWeights": [
+ 30.61,
+ 29.4,
+ 20
+ ],
+ "mcqOtherWeight": 60
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "NONE",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FMcqQuestionE2eT.CS1101",
+ "questionDetails": {
+ "numOfMcqChoices": 0,
+ "mcqChoices": [],
+ "questionText": "Who is the best student?",
+ "questionType": "MCQ",
+ "generateOptionsFor": "STUDENTS",
+ "otherEnabled": false
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "NONE",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/FeedbackMsqQuestionE2ETest.json b/src/e2e/resources/data/FeedbackMsqQuestionE2ETest.json
new file mode 100644
index 00000000000..5404e145a4b
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackMsqQuestionE2ETest.json
@@ -0,0 +1,222 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FMsqQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FMsqQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FMsqQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FMsqQuestionE2eT.instructor",
+ "courseId": "FMsqQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FMsqQuestionE2eT.instructor",
+ "courseId": "FMsqQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FMsqQuestionE2eT.CS2104": {
+ "googleId": "FMsqQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FMsqQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@FMsqQuestionE2eT.CS2104": {
+ "googleId": "FMsqQuestionE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "FMsqQuestionE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "charlie.tmms@FMsqQuestionE2eT.CS2104": {
+ "googleId": "charlie.tmms",
+ "email": "FMsqQuestionE2eT.charlie.tmms@gmail.tmt",
+ "course": "FMsqQuestionE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "danny.tmms@FMsqQuestionE2eT.CS1101": {
+ "googleId": "danny.tmms",
+ "email": "FMsqQuestionE2eT.danny.tmms@gmail.tmt",
+ "course": "FMsqQuestionE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FMsqQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FMsqQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FMsqQuestionE2eT.CS2104",
+ "questionDetails": {
+ "questionType": "MSQ",
+ "questionText": "Which area did this student work well in?",
+ "numOfMsqChoices": 3,
+ "msqChoices": [
+ "Teamwork",
+ "Leadership",
+ "Creativity"
+ ],
+ "otherEnabled": true,
+ "hasAssignedWeights": true,
+ "msqWeights": [
+ 30.61,
+ 29.4,
+ 20
+ ],
+ "msqOtherWeight": 60,
+ "maxSelectableChoices": 2
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "STUDENTS",
+ "numberOfEntitiesToGiveFeedbackTo": 1,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FMsqQuestionE2eT.CS1101",
+ "questionDetails": {
+ "numOfMsqChoices": 0,
+ "msqChoices": [],
+ "questionText": "Which teams did well in your opinion?",
+ "questionType": "MSQ",
+ "generateOptionsFor": "TEAMS",
+ "otherEnabled": false,
+ "minSelectableChoices": 2
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "NONE",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/FeedbackNumScaleQuestionE2ETest.json b/src/e2e/resources/data/FeedbackNumScaleQuestionE2ETest.json
new file mode 100644
index 00000000000..895a7ac3260
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackNumScaleQuestionE2ETest.json
@@ -0,0 +1,210 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FNumScaleQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FNumScaleQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FNumScaleQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FNumScaleQuestionE2eT.instructor",
+ "courseId": "FNumScaleQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FNumScaleQuestionE2eT.instructor",
+ "courseId": "FNumScaleQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FNumScaleQuestionE2eT.CS2104": {
+ "googleId": "FNumScaleQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FNumScaleQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@FNumScaleQuestionE2eT.CS2104": {
+ "googleId": "FNumScaleQuestionE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "FNumScaleQuestionE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "charlie.tmms@FNumScaleQuestionE2eT.CS2104": {
+ "googleId": "charlie.tmms",
+ "email": "FNumScaleQuestionE2eT.charlie.tmms@gmail.tmt",
+ "course": "FNumScaleQuestionE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 3",
+ "section": "None"
+ },
+ "danny.tmms@FNumScaleQuestionE2eT.CS1101": {
+ "googleId": "danny.tmms",
+ "email": "FNumScaleQuestionE2eT.danny.tmms@gmail.tmt",
+ "course": "FNumScaleQuestionE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FNumScaleQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FNumScaleQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FNumScaleQuestionE2eT.CS2104",
+ "questionDetails": {
+ "minScale": 0,
+ "questionText": "Rate this team's product",
+ "questionType": "NUMSCALE",
+ "maxScale": 10,
+ "step": 0.2
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": 1,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FNumScaleQuestionE2eT.CS1101",
+ "questionDetails": {
+ "minScale": 1,
+ "questionText": "Rate this team's teamwork",
+ "questionType": "NUMSCALE",
+ "maxScale": 10,
+ "step": 0.005
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": 1,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/FeedbackRankOptionQuestionE2ETest.json b/src/e2e/resources/data/FeedbackRankOptionQuestionE2ETest.json
new file mode 100644
index 00000000000..e39bf0881bf
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackRankOptionQuestionE2ETest.json
@@ -0,0 +1,220 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FRankOptionQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FRankOptionQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FRankOptionQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FRankOptionQuestionE2eT.instructor",
+ "courseId": "FRankOptionQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FRankOptionQuestionE2eT.instructor",
+ "courseId": "FRankOptionQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FRankOptionQuestionE2eT.CS2104": {
+ "googleId": "FRankOptionQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FRankOptionQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@FRankOptionQuestionE2eT.CS2104": {
+ "googleId": "FRankOptionQuestionE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "FRankOptionQuestionE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "charlie.tmms@FRankOptionQuestionE2eT.CS2104": {
+ "googleId": "charlie.tmms",
+ "email": "FRankOptionQuestionE2eT.charlie.tmms@gmail.tmt",
+ "course": "FRankOptionQuestionE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "danny.tmms@FRankOptionQuestionE2eT.CS1101": {
+ "googleId": "danny.tmms",
+ "email": "FRankOptionQuestionE2eT.danny.tmms@gmail.tmt",
+ "course": "FRankOptionQuestionE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FRankOptionQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FRankOptionQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FRankOptionQuestionE2eT.CS2104",
+ "questionDetails": {
+ "areDuplicatesAllowed": false,
+ "questionText": "As a student, rank for every teammate, the areas of improvement that they should work on",
+ "questionType": "RANK_OPTIONS",
+ "maxOptionsToBeRanked" : 3,
+ "options": [
+ "Teamwork",
+ "Time management",
+ "Quality Control",
+ "Quality Assurance"
+ ]
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "OWN_TEAM_MEMBERS",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FRankOptionQuestionE2eT.CS1101",
+ "questionDetails": {
+ "areDuplicatesAllowed": true,
+ "questionText": "As an student, rank for every team, the areas of improvement that they should work on",
+ "questionType": "RANK_OPTIONS",
+ "minOptionsToBeRanked": 1,
+ "options": [
+ "Teamwork",
+ "Time management",
+ "Quality Control",
+ "Quality Assurance"
+ ]
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/FeedbackRankRecipientQuestionE2ETest.json b/src/e2e/resources/data/FeedbackRankRecipientQuestionE2ETest.json
new file mode 100644
index 00000000000..ffbc8822766
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackRankRecipientQuestionE2ETest.json
@@ -0,0 +1,231 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FRankRecipientQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FRankRecipientQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FRankRecipientQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FRankRecipientQuestionE2eT.instructor",
+ "courseId": "FRankRecipientQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FRankRecipientQuestionE2eT.instructor2",
+ "courseId": "FRankRecipientQuestionE2eT.CS2104",
+ "name": "Teammates Test 2",
+ "email": "tmms.tes2t@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor3": {
+ "googleId": "FRankRecipientQuestionE2eT.instructor",
+ "courseId": "FRankRecipientQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FRankRecipientQuestionE2eT.CS2104": {
+ "googleId": "FRankRecipientQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FRankRecipientQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@FRankRecipientQuestionE2eT.CS2104": {
+ "googleId": "FRankRecipientQuestionE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "FRankRecipientQuestionE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "charlie.tmms@FRankRecipientQuestionE2eT.CS2104": {
+ "googleId": "charlie.tmms",
+ "email": "FRankRecipientQuestionE2eT.charlie.tmms@gmail.tmt",
+ "course": "FRankRecipientQuestionE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "danny.tmms@FRankRecipientQuestionE2eT.CS1101": {
+ "googleId": "danny.tmms",
+ "email": "FRankRecipientQuestionE2eT.danny.tmms@gmail.tmt",
+ "course": "FRankRecipientQuestionE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FRankRecipientQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FRankRecipientQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FRankRecipientQuestionE2eT.CS2104",
+ "questionDetails": {
+ "areDuplicatesAllowed": true,
+ "questionType": "RANK_RECIPIENTS",
+ "questionText": "Rank your instructors by approachability.",
+ "minOptionsToBeRanked": 1
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "INSTRUCTORS",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FRankRecipientQuestionE2eT.CS1101",
+ "questionDetails": {
+ "areDuplicatesAllowed": false,
+ "questionType": "RANK_RECIPIENTS",
+ "questionText": "Rank your instructors by knowledge.",
+ "maxOptionsToBeRanked": 3
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "INSTRUCTORS",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/FeedbackRubricQuestionE2ETest.json b/src/e2e/resources/data/FeedbackRubricQuestionE2ETest.json
new file mode 100644
index 00000000000..23305e42472
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackRubricQuestionE2ETest.json
@@ -0,0 +1,271 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FRubricQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FRubricQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FRubricQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FRubricQuestionE2eT.instructor",
+ "courseId": "FRubricQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FRubricQuestionE2eT.instructor",
+ "courseId": "FRubricQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FRubricQuestionE2eT.CS2104": {
+ "googleId": "FRubricQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FRubricQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@FRubricQuestionE2eT.CS2104": {
+ "googleId": "FRubricQuestionE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "FRubricQuestionE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "charlie.tmms@FRubricQuestionE2eT.CS2104": {
+ "googleId": "charlie.tmms",
+ "email": "FRubricQuestionE2eT.charlie.tmms@gmail.tmt",
+ "course": "FRubricQuestionE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "danny.tmms@FRubricQuestionE2eT.CS1101": {
+ "googleId": "danny.tmms",
+ "email": "FRubricQuestionE2eT.danny.tmms@gmail.tmt",
+ "course": "FRubricQuestionE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FRubricQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FRubricQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FRubricQuestionE2eT.CS2104",
+ "questionDetails": {
+ "rubricSubQuestions": [
+ "This student has done a good job.",
+ "This student has tried his/her best."
+ ],
+ "rubricWeightsForEachCell": [
+ [
+ 1.01,
+ -0.99
+ ],
+ [
+ 100,
+ -200
+ ]
+ ],
+ "questionText": "Please choose the best choice for the following sub-questions.",
+ "numOfRubricChoices": 2,
+ "numOfRubricSubQuestions": 2,
+ "questionType": "RUBRIC",
+ "hasAssignedWeights": true,
+ "rubricChoices": [
+ "Yes",
+ "No"
+ ],
+ "rubricDescriptions": [
+ [
+ "",
+ ""
+ ],
+ [
+ "Most of the time",
+ "Less than half the time"
+ ]
+ ]
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "OWN_TEAM_MEMBERS",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FRubricQuestionE2eT.CS1101",
+ "questionDetails": {
+ "rubricSubQuestions": [
+ "The content of the class was interesting.",
+ "The professor was engaging.",
+ "The duration of the lessons too short."
+ ],
+ "questionText": "Please choose the best choice for the following sub-questions.",
+ "numOfRubricChoices": 5,
+ "numOfRubricSubQuestions": 3,
+ "questionType": "RUBRIC",
+ "hasAssignedWeights": false,
+ "rubricChoices": [
+ "Strongly Agree",
+ "Agree",
+ "Neutral",
+ "Disagree",
+ "Strongly Disagree"
+ ],
+ "rubricDescriptions": [
+ [
+ "",
+ "",
+ "",
+ "",
+ ""
+ ],
+ [
+ "Most of the time",
+ "",
+ "",
+ "",
+ "Less than half the time"
+ ],
+ [
+ "The class should be 1 hour longer.",
+ "The class should be 30 min longer.",
+ "The class duration is just right.",
+ "The class should be 30 min shorter.",
+ "The class should be 1 hour shorter."
+ ]
+ ]
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "NONE",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/test/resources/data/InstructorFeedbackSubmissionEditSaveActionTest.json b/src/e2e/resources/data/FeedbackSubmitPageE2ETest.json
similarity index 51%
rename from src/test/resources/data/InstructorFeedbackSubmissionEditSaveActionTest.json
rename to src/e2e/resources/data/FeedbackSubmitPageE2ETest.json
index ca72c4ea51d..348bb818f03 100644
--- a/src/test/resources/data/InstructorFeedbackSubmissionEditSaveActionTest.json
+++ b/src/e2e/resources/data/FeedbackSubmitPageE2ETest.json
@@ -1,50 +1,64 @@
{
"accounts": {
- "instructor1InCourse1": {
- "googleId": "instructor1InCourse1",
+ "SFSubmitE2eT.instr": {
+ "googleId": "SFSubmitE2eT.instr",
"name": "Teammates Test",
"isInstructor": true,
- "email": "instructor1@course1.tmt",
+ "email": "SFSubmitE2eT.instr@gmail.tmt",
"institute": "TEAMMATES Test Institute 1"
},
- "instructor2InCourse1": {
- "googleId": "instructor2InCourse1",
+ "SFSubmitE2eT.instr2": {
+ "googleId": "SFSubmitE2eT.instr2",
"name": "Teammates Test2",
"isInstructor": true,
- "email": "instructor2@course1.tmt",
+ "email": "SFSubmitE2eT.instr2@gmail.tmt",
"institute": "TEAMMATES Test Institute 1"
},
- "instructor1InCourse2": {
- "googleId": "idOfInstructor1OfCourse2",
- "name": "Instructor 1 of Course 2",
- "isInstructor": true,
- "email": "instr1@course2.tmt",
+ "SFResultsUiT.alice.b": {
+ "googleId": "SFSubmitE2eT.alice.b",
+ "name": "Alice B.",
+ "isInstructor": false,
+ "email": "SFSubmitE2eT.alice.b@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "SFSubmitE2eT.charlie.d": {
+ "googleId": "SFSubmitE2eT.charlie.d",
+ "name": "Charlie D.",
+ "isInstructor": false,
+ "email": "SFSubmitE2eT.charlie.d@gmail.tmt",
"institute": "TEAMMATES Test Institute 1"
},
- "student1InCourse1": {
- "googleId": "student1InCourse1",
- "name": "Student 1 in course 1",
+ "SFSubmitE2eT.danny.e": {
+ "googleId": "SFSubmitE2eT.danny.e",
+ "name": "Danny E.",
"isInstructor": false,
- "email": "student1inCourse1@gmail.tmt",
+ "email": "SFSubmitE2eT.danny.e@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "SFSubmitE2eT.emily.f": {
+ "googleId": "SFSubmitE2eT.emily.f",
+ "name": "Emily F.",
+ "isInstructor": false,
+ "email": "SFSubmitE2eT.emily.f@gmail.tmt",
"institute": "TEAMMATES Test Institute 1"
}
},
"courses": {
- "idOfCourse1": {
- "id": "idOfCourse1",
+ "SFSubmitE2eT.CS2104": {
+ "id": "SFSubmitE2eT.CS2104",
"name": "Programming Language Concepts",
"timeZone": "UTC"
}
},
"instructors": {
- "instructor1InCourse1": {
- "googleId": "instructor1InCourse1",
- "courseId": "idOfCourse1",
+ "SFSubmitE2eT.instr": {
+ "googleId": "SFSubmitE2eT.instr",
+ "courseId": "SFSubmitE2eT.CS2104",
"name": "Teammates Test",
- "email": "instructor1@course1.tmt",
+ "email": "SFSubmitE2eT.instr@gmail.tmt",
"role": "Co-owner",
- "isDisplayedToStudents": false,
- "displayedName": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
"privileges": {
"courseLevel": {
"canviewstudentinsection": true,
@@ -60,37 +74,14 @@
"sessionLevel": {}
}
},
- "instructor2InCourse1": {
- "googleId": "instructor2InCourse1",
- "courseId": "idOfCourse1",
+ "SFSubmitE2eT.instr2": {
+ "googleId": "SFSubmitE2eT.instr2",
+ "courseId": "SFSubmitE2eT.CS2104",
"name": "Teammates Test2",
- "email": "instructor2@course1.tmt",
+ "email": "SFSubmitE2eT.instr2@gmail.tmt",
"role": "Co-owner",
- "isDisplayedToStudents": false,
- "displayedName": "Co-owner",
- "privileges": {
- "courseLevel": {
- "canviewstudentinsection": true,
- "cansubmitsessioninsection": true,
- "canmodifysessioncommentinsection": true,
- "canmodifycourse": true,
- "canviewsessioninsection": true,
- "canmodifysession": true,
- "canmodifystudent": true,
- "canmodifyinstructor": true
- },
- "sectionLevel": {},
- "sessionLevel": {}
- }
- },
- "instructor1InCourse2": {
- "googleId": "idOfInstructor1OfCourse2",
- "courseId": "idOfTypicalCourse2",
- "name": "Instructor1 Course2",
- "email": "instructor1@course2.tmt",
- "role": "Co-owner",
- "isDisplayedToStudents": false,
- "displayedName": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
"privileges": {
"courseLevel": {
"canviewstudentinsection": true,
@@ -108,21 +99,57 @@
}
},
"students": {
- "student1InCourse1": {
- "googleId": "student1InCourse1",
- "email": "student1InCourse1@gmail.tmt",
- "course": "idOfCourse1",
- "name": "student1 In Course1'\"",
- "comments": "comment for student1InCourse1'\"",
- "team": "Team 1.1'\"",
+ "Alice": {
+ "googleId": "SFSubmitE2eT.alice.b",
+ "email": "SFSubmitE2eT.alice.b@gmail.tmt",
+ "course": "SFSubmitE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "Benny": {
+ "googleId": "SFSubmitE2eT.benny.c",
+ "email": "SFSubmitE2eT.benny.c@gmail.tmt",
+ "course": "SFSubmitE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "Charlie": {
+ "googleId": "SFSubmitE2eT.charlie.d",
+ "email": "SFSubmitE2eT.charlie.d@gmail.tmt",
+ "course": "SFSubmitE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "Danny": {
+ "googleId": "SFSubmitE2eT.danny.e",
+ "email": "SFSubmitE2eT.danny.e@gmail.tmt",
+ "course": "SFSubmitE2eT.CS2104",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "Emily": {
+ "googleId": "SFSubmitE2eT.emily.f",
+ "email": "SFSubmitE2eT.emily.f@gmail.tmt",
+ "course": "SFSubmitE2eT.CS2104",
+ "name": "Emily",
+ "comments": "This student's name is Emily",
+ "team": "Team 3",
"section": "None"
}
},
"feedbackSessions": {
"Open Session": {
"feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
- "creatorEmail": "instructor1@course1.tmt",
+ "courseId": "SFSubmitE2eT.CS2104",
+ "creatorEmail": "SFSubmitE2eT.instr@gmail.tmt",
"instructions": "Instructions for first session",
"createdTime": "2012-04-01T23:59:00Z",
"startTime": "2012-04-01T15:59:00Z",
@@ -139,19 +166,19 @@
"isClosingEmailEnabled": true,
"isPublishedEmailEnabled": true
},
- "Closed Session": {
- "feedbackSessionName": "Second Session",
- "courseId": "idOfCourse1",
- "creatorEmail": "instructor1@course1.tmt",
- "instructions": "Please please fill in the second feedback session",
+ "Grace Period Session": {
+ "feedbackSessionName": "Grace Period Session",
+ "courseId": "SFSubmitE2eT.CS2104",
+ "creatorEmail": "SFSubmitE2eT.instr@gmail.tmt",
+ "instructions": "This going to be a grace period session",
"createdTime": "2012-04-01T23:59:00Z",
- "startTime": "2026-04-02T15:59:00Z",
+ "startTime": "2012-04-01T15:59:00Z",
"endTime": "2026-04-30T15:59:00Z",
"sessionVisibleFromTime": "2012-04-01T15:59:00Z",
"resultsVisibleFromTime": "2026-05-01T15:59:00Z",
"timeZone": "Asia/Singapore",
- "gracePeriod": 20,
- "sentOpenEmail": false,
+ "gracePeriod": 10,
+ "sentOpenEmail": true,
"sentClosingEmail": false,
"sentClosedEmail": false,
"sentPublishedEmail": false,
@@ -159,39 +186,19 @@
"isClosingEmailEnabled": true,
"isPublishedEmailEnabled": true
},
- "Empty Session": {
- "feedbackSessionName": "Third Session",
- "courseId": "idOfCourse1",
- "creatorEmail": "instructor1@course1.tmt",
- "instructions": "Please please fill in the last feedback session",
+ "Closed Session": {
+ "feedbackSessionName": "Closed Session",
+ "courseId": "SFSubmitE2eT.CS2104",
+ "creatorEmail": "SFSubmitE2eT.instr@gmail.tmt",
+ "instructions": "This going to be a closed session",
"createdTime": "2012-04-01T23:59:00Z",
"startTime": "2012-04-02T15:59:00Z",
- "endTime": "2026-04-30T15:59:00Z",
+ "endTime": "2012-04-30T15:59:00Z",
"sessionVisibleFromTime": "2012-04-01T15:59:00Z",
- "resultsVisibleFromTime": "2012-05-01T15:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T15:59:00Z",
"timeZone": "Asia/Singapore",
"gracePeriod": 20,
- "sentOpenEmail": true,
- "sentClosingEmail": false,
- "sentClosedEmail": false,
- "sentPublishedEmail": true,
- "isOpeningEmailEnabled": true,
- "isClosingEmailEnabled": true,
- "isPublishedEmailEnabled": true
- },
- "Grace Period Session": {
- "feedbackSessionName": "Session in Grace Period",
- "courseId": "idOfCourse1",
- "creatorEmail": "instructor1@course1.tmt",
- "instructions": "Please please fill in the following questions.",
- "createdTime": "2013-03-20T23:59:00Z",
- "startTime": "2013-06-01T21:59:00Z",
- "endTime": "2026-04-28T21:59:00Z",
- "sessionVisibleFromTime": "2013-03-20T21:59:00Z",
- "resultsVisibleFromTime": "2026-04-29T21:59:00Z",
- "timeZone": "Africa/Johannesburg",
- "gracePeriod": 1440,
- "sentOpenEmail": true,
+ "sentOpenEmail": false,
"sentClosingEmail": false,
"sentClosedEmail": false,
"sentPublishedEmail": false,
@@ -203,43 +210,50 @@
"feedbackQuestions": {
"qn1InSession1": {
"feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
+ "courseId": "SFSubmitE2eT.CS2104",
"questionDetails": {
"questionType": "TEXT",
- "questionText": "What is the best selling point of your product?"
+ "questionText": "Rate 3 other students' products"
},
+ "questionDescription": "Question description for qn1
",
"questionNumber": 1,
- "giverType": "INSTRUCTORS",
- "recipientType": "SELF",
- "numberOfEntitiesToGiveFeedbackTo": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "STUDENTS",
+ "numberOfEntitiesToGiveFeedbackTo": 3,
"showResponsesTo": [
+ "INSTRUCTORS",
"RECEIVER",
- "INSTRUCTORS"
+ "OWN_TEAM_MEMBERS"
],
"showGiverNameTo": [
- "INSTRUCTORS"
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS"
],
"showRecipientNameTo": [
- "INSTRUCTORS"
+ "INSTRUCTORS",
+ "RECEIVER"
]
},
"qn2InSession1": {
"feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
+ "courseId": "SFSubmitE2eT.CS2104",
"questionDetails": {
"questionType": "TEXT",
- "questionText": "Rate 3 students' products"
+ "questionText": "Provide feedback on each instructor"
},
+ "questionDescription": "Question description for qn2
",
"questionNumber": 2,
- "giverType": "INSTRUCTORS",
- "recipientType": "STUDENTS",
- "numberOfEntitiesToGiveFeedbackTo": 3,
+ "giverType": "STUDENTS",
+ "recipientType": "INSTRUCTORS",
+ "numberOfEntitiesToGiveFeedbackTo": -100,
"showResponsesTo": [
"INSTRUCTORS",
- "RECEIVER"
+ "RECEIVER",
+ "OWN_TEAM_MEMBERS"
],
"showGiverNameTo": [
- "INSTRUCTORS"
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS"
],
"showRecipientNameTo": [
"INSTRUCTORS",
@@ -248,29 +262,37 @@
},
"qn3InSession1": {
"feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
+ "courseId": "SFSubmitE2eT.CS2104",
"questionDetails": {
"questionType": "TEXT",
- "questionText": "Comments about the class"
+ "questionText": "Give feedback to your team mates"
},
"questionNumber": 3,
- "giverType": "INSTRUCTORS",
- "recipientType": "NONE",
+ "giverType": "STUDENTS",
+ "recipientType": "OWN_TEAM_MEMBERS",
"numberOfEntitiesToGiveFeedbackTo": -100,
"showResponsesTo": [
- "INSTRUCTORS"
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "RECEIVER"
],
- "showGiverNameTo": [],
"showRecipientNameTo": [
- "INSTRUCTORS"
+ "RECEIVER"
]
},
"qn4InSession1": {
"feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
+ "courseId": "SFSubmitE2eT.CS2104",
"questionDetails": {
- "questionType": "TEXT",
- "questionText": "This question should be hidden."
+ "numOfMcqChoices": 2,
+ "mcqChoices": [
+ "UI",
+ "Algo"
+ ],
+ "questionText": "What do you think is the other teams' best feature?",
+ "questionType": "MCQ",
+ "otherEnabled": false
},
"questionNumber": 4,
"giverType": "TEAMS",
@@ -288,14 +310,14 @@
},
"qn5InSession1": {
"feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
+ "courseId": "SFSubmitE2eT.CS2104",
"questionDetails": {
"questionType": "TEXT",
"questionText": "This question should be hidden."
},
"questionNumber": 5,
- "giverType": "STUDENTS",
- "recipientType": "OWN_TEAM_MEMBERS",
+ "giverType": "INSTRUCTORS",
+ "recipientType": "STUDENTS",
"numberOfEntitiesToGiveFeedbackTo": -100,
"showResponsesTo": [
"RECEIVER"
@@ -307,38 +329,50 @@
"RECEIVER"
]
},
- "qn6InSession1": {
- "feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
+ "qn1InGracePeriodSession": {
+ "feedbackSessionName": "Grace Period Session",
+ "courseId": "SFSubmitE2eT.CS2104",
"questionDetails": {
- "questionType": "TEXT",
- "questionText": "Give feedback to other instructors."
+ "numOfMcqChoices": 2,
+ "mcqChoices": [
+ "UI",
+ "Algo"
+ ],
+ "questionText": "What do you think is the other teams' best feature?",
+ "questionType": "MCQ",
+ "otherEnabled": false
},
- "questionNumber": 6,
- "giverType": "INSTRUCTORS",
- "recipientType": "INSTRUCTORS",
- "numberOfEntitiesToGiveFeedbackTo": -100,
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": 3,
"showResponsesTo": [
+ "INSTRUCTORS",
"RECEIVER"
],
"showGiverNameTo": [
+ "INSTRUCTORS",
"RECEIVER"
],
"showRecipientNameTo": [
+ "INSTRUCTORS",
"RECEIVER"
]
},
- "qn1InSession2": {
- "feedbackSessionName": "Second Session",
- "courseId": "idOfCourse1",
+ "qn2InGracePeriodSession": {
+ "feedbackSessionName": "Grace Period Session",
+ "courseId": "SFSubmitE2eT.CS2104",
"questionDetails": {
- "questionType": "TEXT",
- "questionText": "Give feedback to your instructors"
+ "numOfMcqChoices": 0,
+ "questionText": "Which instructor do you think is the most engaging",
+ "questionType": "MCQ",
+ "otherEnabled": false,
+ "generateOptionsFor": "INSTRUCTORS"
},
- "questionNumber": 1,
- "giverType": "INSTRUCTORS",
- "recipientType": "INSTRUCTORS",
- "numberOfEntitiesToGiveFeedbackTo": -100,
+ "questionNumber": 2,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": 3,
"showResponsesTo": [
"RECEIVER"
],
@@ -349,60 +383,44 @@
"RECEIVER"
]
},
- "qn1InGracePeriodSession": {
- "feedbackSessionName": "Session in Grace Period",
- "courseId": "idOfCourse1",
+ "qn1InClosedSession": {
+ "feedbackSessionName": "Closed Session",
+ "courseId": "SFSubmitE2eT.CS2104",
"questionDetails": {
"questionType": "TEXT",
- "questionText": "Give feedback."
+ "questionText": "What is the best selling point of your product?"
},
"questionNumber": 1,
- "giverType": "INSTRUCTORS",
- "recipientType": "INSTRUCTORS",
- "numberOfEntitiesToGiveFeedbackTo": -100,
+ "giverType": "STUDENTS",
+ "recipientType": "SELF",
+ "numberOfEntitiesToGiveFeedbackTo": 1,
"showResponsesTo": [
- "RECEIVER"
+ "RECEIVER",
+ "OWN_TEAM_MEMBERS",
+ "STUDENTS",
+ "INSTRUCTORS"
],
"showGiverNameTo": [
- "RECEIVER"
+ "RECEIVER",
+ "INSTRUCTORS"
],
"showRecipientNameTo": [
- "RECEIVER"
+ "INSTRUCTORS"
]
}
},
"feedbackResponses": {
- "response1ForQ1S1C1": {
+ "response1": {
"feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
- "feedbackQuestionId": "1",
- "giver": "instructor1@course1.tmt",
- "recipient": "instructor1@course1.tmt",
- "responseDetails": {
- "questionType": "TEXT",
- "answer": "Instructor self feedback."
- }
- },
- "response1ForQ2S1C1": {
- "feedbackSessionName": "First Session",
- "courseId": "idOfCourse1",
- "feedbackQuestionId": "2",
- "giver": "instructor1@course1.tmt",
- "recipient": "student1inCourse1@gmail.tmt",
- "responseDetails": {
- "questionType": "TEXT",
- "answer": "Instructor feedback to student 1."
- }
- },
- "response1ForGracePeriodSession": {
- "feedbackSessionName": "Session in Grace Period",
- "courseId": "idOfCourse1",
+ "courseId": "SFSubmitE2eT.CS2104",
"feedbackQuestionId": "1",
- "giver": "instructor1@course1.tmt",
- "recipient": "instructor2@course1.tmt",
+ "giver": "SFSubmitE2eT.benny.c@gmail.tmt",
+ "recipient": "SFSubmitE2eT.alice.b@gmail.tmt",
+ "giverSection": "None",
+ "recipientSection": "None",
"responseDetails": {
"questionType": "TEXT",
- "answer": "Response from instructor 1 to instructor 2."
+ "answer": "Alice response."
}
}
},
diff --git a/src/e2e/resources/data/FeedbackTextQuestionE2ETest.json b/src/e2e/resources/data/FeedbackTextQuestionE2ETest.json
new file mode 100644
index 00000000000..d85306baf84
--- /dev/null
+++ b/src/e2e/resources/data/FeedbackTextQuestionE2ETest.json
@@ -0,0 +1,198 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "FTextQuestionE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "FTextQuestionE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "FTextQuestionE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "FTextQuestionE2eT.instructor",
+ "courseId": "FTextQuestionE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "FTextQuestionE2eT.instructor2",
+ "courseId": "FTextQuestionE2eT.CS2104",
+ "name": "Teammates Test 2",
+ "email": "tmms.test2@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor3": {
+ "googleId": "FTextQuestionE2eT.instructor",
+ "courseId": "FTextQuestionE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@FTextQuestionE2eT.CS2104": {
+ "googleId": "FTextQuestionE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "FTextQuestionE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FTextQuestionE2eT.CS2104",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FTextQuestionE2eT.CS1101",
+ "creatorEmail": "tmms.test@gmail.tmt",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1ForFirstSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "FTextQuestionE2eT.CS2104",
+ "questionDetails": {
+ "questionType": "TEXT",
+ "questionText": "What did this instructor do well?",
+ "recommendedLength": 1000
+ },
+ "questionDescription": "Testing description for first session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "INSTRUCTORS",
+ "numberOfEntitiesToGiveFeedbackTo": 1,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ },
+ "qn1ForSecondSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "FTextQuestionE2eT.CS1101",
+ "questionDetails": {
+ "questionType": "TEXT",
+ "questionText": "How can this instructor improve?",
+ "recommendedLength": 100
+ },
+ "questionDescription": "Testing description for second session
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "INSTRUCTORS",
+ "numberOfEntitiesToGiveFeedbackTo": 1,
+ "showResponsesTo": [
+ "INSTRUCTORS"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/InstructorCourseDetailsPageE2ETest.json b/src/e2e/resources/data/InstructorCourseDetailsPageE2ETest.json
new file mode 100644
index 00000000000..3bb8ae8fd34
--- /dev/null
+++ b/src/e2e/resources/data/InstructorCourseDetailsPageE2ETest.json
@@ -0,0 +1,130 @@
+{
+ "accounts": {
+ "ICDetailsE2eT.instr": {
+ "googleId": "ICDetailsE2eT.instr",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "ICDetailsE2eT.instr@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "ICDetailsE2eT.instr2": {
+ "googleId": "ICDetailsE2eT.instr2",
+ "name": "Teammates Test 2",
+ "isInstructor": true,
+ "email": "ICDetailsE2eT.instr2@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "Alice": {
+ "googleId": "ICDetailsE2eT.alice.b",
+ "name": "Alice Betsy",
+ "isInstructor": false,
+ "email": "alice.b.tmms@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "Danny": {
+ "googleId": "ICDetailsE2eT.danny.e",
+ "name": "Charlie Davis",
+ "isInstructor": false,
+ "email": "danny.e.tmms@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "ICDetailsE2eT.CS2104": {
+ "id": "ICDetailsE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "UTC"
+ }
+ },
+ "instructors": {
+ "ICDetailsE2eT.instr": {
+ "googleId": "ICDetailsE2eT.instr",
+ "courseId": "ICDetailsE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "ICDetailsE2eT.instr@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "ICDetailsE2eT.instr2": {
+ "googleId": "ICDetailsE2eT.instr2",
+ "courseId": "ICDetailsE2eT.CS2104",
+ "name": "Teammates Test 2",
+ "email": "ICDetailsE2eT.instr2@gmail.tmt",
+ "role": "Tutor",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": false,
+ "canmodifycourse": false,
+ "canviewsessioninsection": true,
+ "canmodifysession": false,
+ "canmodifystudent": false,
+ "canmodifyinstructor": false
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@ICDetailsE2eT.CS2104": {
+ "googleId": "ICDetailsE2eT.alice.b",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "ICDetailsE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@ICDetailsE2eT.CS2104": {
+ "googleId": "",
+ "email": "benny.c.tmms@gmail.tmt",
+ "course": "ICDetailsE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "charlie.tmms@ICDetailsE2eT.CS2104": {
+ "googleId": "",
+ "email": "charlie.d.tmms@gmail.tmt",
+ "course": "ICDetailsE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 1",
+ "section": "Section 1"
+ },
+ "danny.tmms@ICDetailsE2eT.CS2104": {
+ "googleId": "ICDetailsE2eT.danny.e",
+ "email": "danny.e.tmms@gmail.tmt",
+ "course": "ICDetailsE2eT.CS2104",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "Section 2"
+ }
+ },
+ "feedbackSessions": {},
+ "feedbackQuestions": {},
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/test/resources/data/AdminAccountManagementPageUiTest.json b/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json
similarity index 62%
rename from src/test/resources/data/AdminAccountManagementPageUiTest.json
rename to src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json
index f801d1870c5..84c7e43f8b2 100644
--- a/src/test/resources/data/AdminAccountManagementPageUiTest.json
+++ b/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json
@@ -1,33 +1,25 @@
{
"accounts": {
- "AAMgtUiT.instr1": {
- "googleId": "AAMgtUiT.instr1",
- "name": "Teammates Instr1",
+ "ICJConfirmationE2eT.instr": {
+ "googleId": "ICJConfirmationE2eT.instr",
+ "name": "Teammates Test",
"isInstructor": true,
- "email": "AAMgtUiT.instr1@gmail.tmt",
- "institute": "TEAMMATES Test Institute 1"
- },
- "AAMgtUiT.instr3": {
- "googleId": "AAMgtUiT.instr3",
- "name": "Teammates Instr3",
- "isInstructor": true,
- "email": "AAMgtUiT.instr3@gmail.tmt",
+ "email": "ICJConfirmationE2eT.instr@gmail.tmt",
"institute": "TEAMMATES Test Institute 1"
}
},
"courses": {
- "AAMgtUiT.CS2104": {
- "id": "AAMgtUiT.CS2104",
- "name": "Programming Language Concepts",
+ "ICJConfirmationE2eT.CS1101": {
+ "id": "ICJConfirmationE2eT.CS1101",
+ "name": "Programming Methodology",
"timeZone": "UTC"
}
},
"instructors": {
- "AAMgtUiT.instr1": {
- "googleId": "AAMgtUiT.instr1",
- "courseId": "AAMgtUiT.CS2104",
- "name": "Teammates Instr1",
- "email": "AAMgtUiT.instr1@gmail.tmt",
+ "ICJConfirmationE2eT.instr.CS1101": {
+ "courseId": "ICJConfirmationE2eT.CS1101",
+ "name": "Teammates Test 2",
+ "email": "ICJConfirmationE2eT.instr2@gmail.tmt",
"role": "Co-owner",
"isDisplayedToStudents": false,
"displayedName": "Co-owner",
@@ -46,13 +38,13 @@
"sessionLevel": {}
}
},
- "AAMgtUiT.instr3": {
- "googleId": "AAMgtUiT.instr3",
- "courseId": "AAMgtUiT.CS2104",
- "name": "Teammates Instr3",
- "email": "AAMgtUiT.instr2@gmail.tmt",
+ "ICJConfirmationE2eT.instr2.CS1101": {
+ "googleId": "ICJConfirmationE2eT.instr",
+ "courseId": "ICJConfirmationE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "ICJConfirmationE2eT.instr@gmail.tmt",
"role": "Co-owner",
- "isDisplayedToStudents": false,
+ "isDisplayedToStudents": true,
"displayedName": "Co-owner",
"privileges": {
"courseLevel": {
diff --git a/src/test/resources/data/InstructorCourseStudentDetailsEditPageUiTest.json b/src/e2e/resources/data/InstructorCourseStudentDetailsEditPageE2ETest.json
similarity index 56%
rename from src/test/resources/data/InstructorCourseStudentDetailsEditPageUiTest.json
rename to src/e2e/resources/data/InstructorCourseStudentDetailsEditPageE2ETest.json
index 84f8f8a1d70..f101b9ac287 100644
--- a/src/test/resources/data/InstructorCourseStudentDetailsEditPageUiTest.json
+++ b/src/e2e/resources/data/InstructorCourseStudentDetailsEditPageE2ETest.json
@@ -1,26 +1,26 @@
{
"accounts": {
- "CCSDEditUiT.instr": {
- "googleId": "CCSDEditUiT.instr",
+ "ICSDEditE2eT.instr": {
+ "googleId": "ICSDEditE2eT.instr",
"name": "Teammates Test",
"isInstructor": true,
- "email": "CCSDEditUiT.instr@gmail.tmt",
+ "email": "ICSDEditE2eT.instr@gmail.tmt",
"institute": "TEAMMATES Test Institute 1"
}
},
"courses": {
- "CCSDEditUiT.CS2104": {
- "id": "CCSDEditUiT.CS2104",
+ "ICSDEditE2eT.CS2104": {
+ "id": "ICSDEditE2eT.CS2104",
"name": "Programming Language Concepts",
"timeZone": "UTC"
}
},
"instructors": {
- "CCSDEditUiT.instr": {
- "googleId": "CCSDEditUiT.instr",
- "courseId": "CCSDEditUiT.CS2104",
+ "ICSDEditE2eT.instr": {
+ "googleId": "ICSDEditE2eT.instr",
+ "courseId": "ICSDEditE2eT.CS2104",
"name": "Teammates Test",
- "email": "CCSDEditUiT.instr@gmail.tmt",
+ "email": "ICSDEditE2eT.instr@gmail.tmt",
"role": "Co-owner",
"isDisplayedToStudents": false,
"displayedName": "Co-owner",
@@ -41,23 +41,21 @@
}
},
"students": {
- "registeredStudent": {
- "googleId": "CCSDEditUiT.jose.tmms",
- "email": "CCSDEditUiT.jose.tmms@gmail.tmt",
- "course": "CCSDEditUiT.CS2104",
- "name": "José Gómez'\"",
- "comments": "This student's name is José Gómez'\"",
- "team": "Team 1'\"",
- "section": "None"
+ "ICSDEditE2eT.jose.tmms": {
+ "googleId": "ICSDEditE2eT.jose.tmms",
+ "email": "ICSDEditE2eT.jose.tmms@gmail.tmt",
+ "course": "ICSDEditE2eT.CS2104",
+ "name": "José Gómez",
+ "comments": "This student's name is José Gómez",
+ "team": "Team 1"
},
- "unregisteredStudent": {
+ "ICSDEditE2eT.benny.c": {
"googleId": "",
- "email": "benny.c.tmms@gmail.tmt",
- "course": "CCSDEditUiT.CS2104",
+ "email": "ICSDEditE2eT.benny.c.tmms@gmail.tmt",
+ "course": "ICSDEditE2eT.CS2104",
"name": "Benny Charles",
"comments": "This student's name is Benny Charles",
- "team": "Team 1'\"",
- "section": "None"
+ "team": "Team 1"
}
},
"feedbackSessions": {},
diff --git a/src/e2e/resources/data/InstructorCourseStudentDetailsPageE2ETest.json b/src/e2e/resources/data/InstructorCourseStudentDetailsPageE2ETest.json
new file mode 100644
index 00000000000..c95c670cf17
--- /dev/null
+++ b/src/e2e/resources/data/InstructorCourseStudentDetailsPageE2ETest.json
@@ -0,0 +1,79 @@
+{
+ "accounts": {
+ "ICSDetailsE2eT.instr": {
+ "googleId": "ICSDetailsE2eT.instr.veryLongGoogleId",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "ICSDetailsE2eT.instr@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "ICSDetailsE2eT.CS2104": {
+ "id": "ICSDetailsE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "UTC"
+ }
+ },
+ "instructors": {
+ "ICSDetailsE2eT.instr": {
+ "googleId": "ICSDetailsE2eT.instr",
+ "courseId": "ICSDetailsE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "ICSDetailsE2eT.instr@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "ICSDetailsE2eT.jose.tmms": {
+ "googleId": "ICSDetailsE2eT.jose.tmms",
+ "email": "ICSDetailsE2eT.jose.tmms@gmail.tmt",
+ "course": "ICSDetailsE2eT.CS2104",
+ "name": "José Gómez",
+ "comments": "This student's name is José Gómez",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "ICSDetailsE2eT.benny.c": {
+ "googleId": "ICSDetailsE2eT.benny.c",
+ "email": "ICSDetailsE2eT.benny.c.tmms@gmail.tmt",
+ "course": "ICSDetailsE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "Section 1"
+ }
+ },
+ "feedbackSessions": {},
+ "feedbackQuestions": {},
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {
+ "ICSDetailsE2eT.jose.tmms": {
+ "googleId": "ICSDetailsE2eT.jose.tmms",
+ "shortName": "José",
+ "email": "jose@e.tmt",
+ "institute": "TEAMMATES Test Institute 7",
+ "nationality": "Laotian",
+ "gender": "MALE",
+ "moreInfo": "This is a lot of info...",
+ "pictureKey": ""
+ }
+ }
+}
diff --git a/src/e2e/resources/data/InstructorFeedbackEditPageE2ETest.json b/src/e2e/resources/data/InstructorFeedbackEditPageE2ETest.json
new file mode 100644
index 00000000000..8c355e4baa1
--- /dev/null
+++ b/src/e2e/resources/data/InstructorFeedbackEditPageE2ETest.json
@@ -0,0 +1,187 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "CFeedbackEditE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "CFeedbackEditE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "CFeedbackEditE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "CFeedbackEditE2eT.instructor",
+ "courseId": "CFeedbackEditE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "CFeedbackEditE2eT.instructor",
+ "courseId": "CFeedbackEditE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@CFeedbackEditE2eT.CS2104": {
+ "googleId": "CFeedbackEditE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "CFeedbackEditE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@CFeedbackEditE2eT.CS2104": {
+ "googleId": "CFeedbackEditE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "CFeedbackEditE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "charlie.tmms@CFeedbackEditE2eT.CS1101": {
+ "googleId": "charlie.tmms",
+ "email": "CFeedbackEditE2eT.charlie.tmms@gmail.tmt",
+ "course": "CFeedbackEditE2eT.CS1101",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "danny.tmms@CFeedbackEditE2eT.CS1101": {
+ "googleId": "danny.tmms",
+ "email": "CFeedbackEditE2eT.danny.tmms@gmail.tmt",
+ "course": "CFeedbackEditE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "openSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "CFeedbackEditE2eT.CS2104",
+ "creatorEmail": "backdoor@teammates.com",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession2": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "CFeedbackEditE2eT.CS1101",
+ "creatorEmail": "backdoor@teammates.com",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "CFeedbackEditE2eT.CS1101",
+ "questionDetails": {
+ "questionType": "TEXT",
+ "questionText": "Testing question text"
+ },
+ "questionDescription": "Testing description
",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": 2,
+ "showResponsesTo": [
+ "STUDENTS",
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "STUDENTS",
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS",
+ "RECEIVER"
+ ],
+ "showRecipientNameTo": [
+ "STUDENTS",
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS",
+ "RECEIVER"
+ ]
+ }
+ },
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/InstructorFeedbackSessionsPageE2ETest.json b/src/e2e/resources/data/InstructorFeedbackSessionsPageE2ETest.json
new file mode 100644
index 00000000000..05ccd8410f4
--- /dev/null
+++ b/src/e2e/resources/data/InstructorFeedbackSessionsPageE2ETest.json
@@ -0,0 +1,201 @@
+{
+ "accounts": {
+ "instructorWithSessions": {
+ "googleId": "CFeedbackSessionsE2eT.instructor",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "tmms.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "course": {
+ "id": "CFeedbackSessionsE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "Africa/Johannesburg"
+ },
+ "course2": {
+ "id": "CFeedbackSessionsE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "Africa/Johannesburg"
+ }
+ },
+ "instructors": {
+ "instructor": {
+ "googleId": "CFeedbackSessionsE2eT.instructor",
+ "courseId": "CFeedbackSessionsE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor2": {
+ "googleId": "CFeedbackSessionsE2eT.instructor",
+ "courseId": "CFeedbackSessionsE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "tmms.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@CFeedbackSessionsE2eT.CS2104": {
+ "googleId": "CFeedbackSessionsE2eT.alice.tmms",
+ "email": "alice.b.tmms@gmail.tmt",
+ "course": "CFeedbackSessionsE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "benny.tmms@CFeedbackSessionsE2eT.CS2104": {
+ "googleId": "CFeedbackSessionsE2eT.benny.tmms",
+ "email": "benny.tmms@gmail.tmt",
+ "course": "CFeedbackSessionsE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "charlie.tmms@CFeedbackSessionsE2eT.CS1101": {
+ "googleId": "CFeedbackSessionsE2eT.charlie.tmms",
+ "email": "charlie.tmms@gmail.tmt",
+ "course": "CFeedbackSessionsE2eT.CS1101",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "danny.tmms@CFeedbackSessionsE2eT.CS1101": {
+ "googleId": "CFeedbackSessionsE2eT.danny.tmms",
+ "email": "CFeedbackSessionsE2eT.danny.tmms@gmail.tmt",
+ "course": "CFeedbackSessionsE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "closedSession": {
+ "feedbackSessionName": "First Session",
+ "courseId": "CFeedbackSessionsE2eT.CS2104",
+ "creatorEmail": "backdoor@teammates.com",
+ "instructions": "Instructions for first session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2016-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2016-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "openSession": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "CFeedbackSessionsE2eT.CS1101",
+ "creatorEmail": "backdoor@teammates.com",
+ "instructions": "Instructions for Second session
",
+ "createdTime": "2012-04-01T23:59:00Z",
+ "startTime": "2012-04-01T21:59:00Z",
+ "endTime": "2026-04-30T21:59:00Z",
+ "sessionVisibleFromTime": "2012-04-01T21:59:00Z",
+ "resultsVisibleFromTime": "2026-05-01T21:59:00Z",
+ "timeZone": "Africa/Johannesburg",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "qn1": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "CFeedbackSessionsE2eT.CS1101",
+ "questionDetails": {
+ "questionType": "TEXT",
+ "questionText": "Testing question text"
+ },
+ "questionDescription": "Testing description",
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "TEAMS",
+ "numberOfEntitiesToGiveFeedbackTo": 2,
+ "showResponsesTo": [
+ "STUDENTS",
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "STUDENTS",
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS",
+ "RECEIVER"
+ ],
+ "showRecipientNameTo": [
+ "STUDENTS",
+ "INSTRUCTORS",
+ "OWN_TEAM_MEMBERS",
+ "RECEIVER"
+ ]
+ }
+ },
+ "feedbackResponses": {
+ "response1": {
+ "feedbackSessionName": "Second Session",
+ "courseId": "CFeedbackSessionsE2eT.CS1101",
+ "feedbackQuestionId": "1",
+ "giver": "CFeedbackSessionsE2eT.danny.tmms@gmail.tmt",
+ "recipient": "CFeedbackSessionsE2eT.danny.tmms@gmail.tmt",
+ "giverSection": "None",
+ "recipientSection": "None",
+ "responseDetails": {
+ "questionType": "TEXT",
+ "answer": "Danny self feedback."
+ }
+ }
+ },
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/InstructorHomePageE2ETest.json b/src/e2e/resources/data/InstructorHomePageE2ETest.json
new file mode 100644
index 00000000000..597858388da
--- /dev/null
+++ b/src/e2e/resources/data/InstructorHomePageE2ETest.json
@@ -0,0 +1,314 @@
+{
+ "accounts": {
+ "IHomeE2eT.instr": {
+ "googleId": "IHomeE2eT.instructor.tmms",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "IHomeE2eT.instructor.tmms@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "IHomeE2eT.CS2104": {
+ "createdAt": "2012-04-01T23:58:00Z",
+ "id": "IHomeE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "UTC"
+ },
+ "IHomeE2eT.CS1101": {
+ "createdAt": "2013-04-01T23:59:00Z",
+ "id": "IHomeE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "UTC"
+ }
+ },
+ "instructors": {
+ "IHomeE2eT.instr.CS2104": {
+ "googleId": "IHomeE2eT.instructor.tmms",
+ "courseId": "IHomeE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "IHomeE2eT.instructor.tmms@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "IHomeE2eT.instr.CS1101": {
+ "googleId": "IHomeE2eT.instructor.tmms",
+ "courseId": "IHomeE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "IHomeE2eT.instructor.tmms@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "IHomeE2eT.alice.b.tmms@IHomeE2eT.CS2104": {
+ "googleId": "IHomeE2eT.alice.b.tmms",
+ "email": "IHomeE2eT.alice.b.tmms@gmail.tmt",
+ "course": "IHomeE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "IHomeE2eT.benny.c.tmms@IHomeE2eT.CS2104": {
+ "googleId": "IHomeE2eT.benny.c.tmms",
+ "email": "IHomeE2eT.benny.c.tmms@gmail.tmt",
+ "course": "IHomeE2eT.CS2104",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "IHomeE2eT.charlie.d.tmms@IHomeE2eT.CS2104": {
+ "googleId": "IHomeE2eT.charlie.d.tmms",
+ "email": "IHomeE2eT.charlie.d.tmms@gmail.tmt",
+ "course": "IHomeE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "IHomeE2eT.danny.e.tmms@IHomeE2eT.CS2104": {
+ "googleId": "IHomeE2eT.danny.e.tmms",
+ "email": "IHomeE2eT.danny.e.tmms@gmail.tmt",
+ "course": "IHomeE2eT.CS2104",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "IHomeE2eT.alice.b.tmms@IHomeE2eT.CS1101": {
+ "googleId": "IHomeE2eT.alice.b.tmms",
+ "email": "IHomeE2eT.alice.b.tmms@gmail.tmt",
+ "course": "IHomeE2eT.CS1101",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "IHomeE2eT.benny.c.tmms@IHomeE2eT.CS1101": {
+ "googleId": "IHomeE2eT.benny.c.tmms",
+ "email": "IHomeE2eT.benny.c.tmms@gmail.tmt",
+ "course": "IHomeE2eT.CS1101",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "IHomeE2eT.charlie.d.tmms@IHomeE2eT.CS1101": {
+ "googleId": "IHomeE2eT.charlie.d.tmms",
+ "email": "IHomeE2eT.charlie.d.tmms@gmail.tmt",
+ "course": "IHomeE2eT.CS1101",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 2",
+ "section": "None"
+ },
+ "IHomeE2eT.danny.e.tmms@IHomeE2eT.CS1101": {
+ "googleId": "IHomeE2eT.danny.e.tmms",
+ "email": "IHomeE2eT.danny.e.tmms@gmail.tmt",
+ "course": "IHomeE2eT.CS1101",
+ "name": "Danny Engrid",
+ "comments": "This student's name is Danny Engrid",
+ "team": "Team 2",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {
+ "First Feedback Session": {
+ "feedbackSessionName": "First Feedback Session",
+ "courseId": "IHomeE2eT.CS2104",
+ "creatorEmail": "IHomeE2eT.instructor.tmms@gmail.tmt",
+ "instructions": "Please please fill in the following questions.",
+ "createdTime": "2012-03-20T23:59:00Z",
+ "startTime": "2012-04-01T04:00:00Z",
+ "endTime": "2027-04-30T15:59:00Z",
+ "sessionVisibleFromTime": "2012-03-28T15:59:00Z",
+ "resultsVisibleFromTime": "2027-05-01T15:59:00Z",
+ "timeZone": "Asia/Singapore",
+ "gracePeriod": 10,
+ "sentOpenEmail": true,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "Second Feedback Session": {
+ "feedbackSessionName": "Second Feedback Session",
+ "courseId": "IHomeE2eT.CS2104",
+ "creatorEmail": "IHomeE2eT.instructor.tmms@gmail.tmt",
+ "instructions": "Please please fill in the following questions.",
+ "createdTime": "2012-03-20T23:59:00Z",
+ "startTime": "2027-03-29T14:59:00Z",
+ "endTime": "2027-04-30T04:00:00Z",
+ "sessionVisibleFromTime": "2012-03-28T15:59:00Z",
+ "resultsVisibleFromTime": "2027-05-01T15:59:00Z",
+ "timeZone": "Asia/Singapore",
+ "gracePeriod": 10,
+ "sentOpenEmail": false,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "Third Feedback Session": {
+ "feedbackSessionName": "Third Feedback Session",
+ "courseId": "IHomeE2eT.CS2104",
+ "creatorEmail": "IHomeE2eT.instructor.tmms@gmail.tmt",
+ "instructions": "Please please fill in the following questions.",
+ "createdTime": "2012-03-20T23:59:00Z",
+ "startTime": "2012-04-10T15:59:00Z",
+ "endTime": "2012-04-30T15:59:00Z",
+ "sessionVisibleFromTime": "2012-03-28T15:59:00Z",
+ "resultsVisibleFromTime": "2027-05-01T15:59:00Z",
+ "timeZone": "Asia/Singapore",
+ "gracePeriod": 10,
+ "sentOpenEmail": true,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": false,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "Fourth Feedback Session": {
+ "feedbackSessionName": "Fourth Feedback Session",
+ "courseId": "IHomeE2eT.CS2104",
+ "creatorEmail": "IHomeE2eT.instructor.tmms@gmail.tmt",
+ "instructions": "Please please fill in the following questions.",
+ "createdTime": "2012-03-20T23:59:00Z",
+ "startTime": "2012-04-05T03:59:00Z",
+ "endTime": "2012-04-20T04:00:00Z",
+ "sessionVisibleFromTime": "2012-03-28T15:59:00Z",
+ "resultsVisibleFromTime": "2012-05-01T15:59:00Z",
+ "timeZone": "Asia/Singapore",
+ "gracePeriod": 10,
+ "sentOpenEmail": true,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": true,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ },
+ "CS1101 Session": {
+ "feedbackSessionName": "CS1101 Session",
+ "courseId": "IHomeE2eT.CS1101",
+ "creatorEmail": "IHomeE2eT.instructor.tmms@gmail.tmt",
+ "instructions": "Please please fill in the following questions.",
+ "createdTime": "2012-03-20T23:59:00Z",
+ "startTime": "2012-04-05T03:59:00Z",
+ "endTime": "2012-04-20T04:00:00Z",
+ "sessionVisibleFromTime": "2012-03-28T15:59:00Z",
+ "resultsVisibleFromTime": "2012-05-01T15:59:00Z",
+ "timeZone": "Asia/Singapore",
+ "gracePeriod": 10,
+ "sentOpenEmail": true,
+ "sentClosingEmail": false,
+ "sentClosedEmail": false,
+ "sentPublishedEmail": true,
+ "isOpeningEmailEnabled": true,
+ "isClosingEmailEnabled": true,
+ "isPublishedEmailEnabled": true
+ }
+ },
+ "feedbackQuestions": {
+ "IHomeE2eT.CS2104:First Feedback Session:Q1": {
+ "feedbackSessionName": "First Feedback Session",
+ "courseId": "IHomeE2eT.CS2104",
+ "questionDetails": {
+ "questionType": "TEXT",
+ "questionText": "Rate 5 other students' products"
+ },
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "STUDENTS",
+ "numberOfEntitiesToGiveFeedbackTo": 5,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ },
+ "IHomeE2eT.CS2104:Third Feedback Session:Q1": {
+ "feedbackSessionName": "Third Feedback Session",
+ "courseId": "IHomeE2eT.CS2104",
+ "questionDetails": {
+ "questionType": "TEXT",
+ "questionText": "Rate 5 other students' products"
+ },
+ "questionNumber": 1,
+ "giverType": "STUDENTS",
+ "recipientType": "STUDENTS",
+ "numberOfEntitiesToGiveFeedbackTo": 5,
+ "showResponsesTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ],
+ "showGiverNameTo": [
+ "INSTRUCTORS"
+ ],
+ "showRecipientNameTo": [
+ "INSTRUCTORS",
+ "RECEIVER"
+ ]
+ }
+ },
+ "feedbackResponses": {
+ "response1": {
+ "feedbackSessionName": "First Feedback Session",
+ "courseId": "IHomeE2eT.CS2104",
+ "feedbackQuestionId": "1",
+ "giver": "IHomeE2eT.alice.b.tmms@gmail.tmt",
+ "recipient": "IHomeE2eT.danny.e.tmms@gmail.tmt",
+ "giverSection": "None",
+ "recipientSection": "None",
+ "responseDetails": {
+ "questionType": "TEXT",
+ "answer": "Alice response to Danny."
+ }
+ }
+ },
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/InstructorSearchPageE2ETest.json b/src/e2e/resources/data/InstructorSearchPageE2ETest.json
new file mode 100644
index 00000000000..2f9649ccbee
--- /dev/null
+++ b/src/e2e/resources/data/InstructorSearchPageE2ETest.json
@@ -0,0 +1,189 @@
+{
+ "accounts": {
+ "instructor1OfCourse1": {
+ "googleId": "searchE2E.idOfInstructor1OfCourse1",
+ "name": "Instructor 1 of Course 1",
+ "isInstructor": true,
+ "email": "searchE2E.instr1@course1.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "student1InCourse1": {
+ "googleId": "searchE2E.student1InCourse1",
+ "name": "Student 1 in course 1",
+ "isInstructor": false,
+ "email": "searchE2E.student1InCourse1@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "student2InCourse1": {
+ "googleId": "searchE2E.student2InCourse1",
+ "name": "Student in two courses",
+ "isInstructor": false,
+ "email": "searchE2E.student2InCourse1@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "student2.2InCourse1": {
+ "googleId": "searchE2E.student2.2InCourse1",
+ "name": "Student in two courses",
+ "isInstructor": false,
+ "email": "searchE2E.student2InCourse1@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "student3InCourse1": {
+ "googleId": "searchE2E.student3InCourse1",
+ "name": "Student 3 in course 1",
+ "isInstructor": false,
+ "email": "searchE2E.student3InCourse1@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "typicalCourse1": {
+ "id": "searchE2E.idOfTypicalCourse1",
+ "name": "Typical Course 1 with 2 Evals",
+ "timeZone": "UTC"
+ },
+ "typicalCourse2": {
+ "id": "searchE2E.idOfTypicalCourse2",
+ "name": "Typical Course 2 with 2 Evals",
+ "timeZone": "UTC"
+ }
+ },
+ "instructors": {
+ "instructor1OfCourse1": {
+ "googleId": "searchE2E.idOfInstructor1OfCourse1",
+ "courseId": "searchE2E.idOfTypicalCourse1",
+ "name": "Instructor1 Course1",
+ "email": "searchE2E.instructor1@course1.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructor1OfCourse2": {
+ "googleId": "searchE2E.idOfInstructor1OfCourse1",
+ "courseId": "searchE2E.idOfTypicalCourse2",
+ "name": "Instructor1 Course2",
+ "email": "searchE2E.instructor1@course1.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "student1InCourse1": {
+ "googleId": "searchE2E.student1InCourse1",
+ "email": "searchE2E.student1InCourse1@gmail.tmt",
+ "course": "searchE2E.idOfTypicalCourse1",
+ "name": "student1 In Course1",
+ "comments": "comment for student1InCourse1",
+ "team": "Team 1.1",
+ "section": "Section 1"
+ },
+ "student2InCourse1": {
+ "googleId": "searchE2E.student2InCourse1",
+ "email": "searchE2E.student2InCourse1@gmail.tmt",
+ "course": "searchE2E.idOfTypicalCourse1",
+ "name": "student2 In Course1",
+ "comments": "",
+ "team": "Team 1.1",
+ "section": "Section 1"
+ },
+ "student2.2InCourse1": {
+ "googleId": "searchE2E.student2.2InCourse1",
+ "email": "searchE2E.student2.2InCourse1@gmail.tmt",
+ "course": "searchE2E.idOfTypicalCourse1",
+ "name": "student2 2 In Course1",
+ "comments": "",
+ "team": "Team 1.1",
+ "section": "Section 1"
+ },
+ "student3InCourse1": {
+ "googleId": "searchE2E.student3InCourse1",
+ "email": "searchE2E.student3InCourse1@gmail.tmt",
+ "course": "searchE2E.idOfTypicalCourse1",
+ "name": "student3 In Course1",
+ "comments": "",
+ "team": "Team 1.2",
+ "section": "Section 1"
+ },
+ "student1InCourse2": {
+ "googleId": "",
+ "email": "searchE2E.student1InCourse2@gmail.tmt",
+ "course": "searchE2E.idOfTypicalCourse2",
+ "name": "student1 In Course2",
+ "comments": "comment for student1InCourse2",
+ "team": "Team 1.1",
+ "section": "Section 1"
+ },
+ "student2InCourse2": {
+ "googleId": "",
+ "email": "searchE2E.student2InCourse2@gmail.tmt",
+ "course": "searchE2E.idOfTypicalCourse2",
+ "name": "student2 In Course2",
+ "comments": "",
+ "team": "Team 1.1",
+ "section": "Section 1"
+ },
+ "student2.2InCourse2": {
+ "googleId": "",
+ "email": "searchE2E.student2.2InCourse2@gmail.tmt",
+ "course": "searchE2E.idOfTypicalCourse2",
+ "name": "student2 2 In Course2",
+ "comments": "",
+ "team": "Team 1.1",
+ "section": "Section 1"
+ },
+ "student3InCourse2": {
+ "googleId": "",
+ "email": "searchE2E.student3InCourse2@gmail.tmt",
+ "course": "searchE2E.idOfTypicalCourse2",
+ "name": "student3 In Course2",
+ "comments": "",
+ "team": "Team 1.2",
+ "section": "Section 1"
+ }
+ },
+ "feedbackSessions": {},
+ "feedbackQuestions": {},
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {
+ "student1InCourse1": {
+ "googleId": "student1InCourse1",
+ "shortName": "Stud1",
+ "email": "i.m.stud1@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 3",
+ "nationality": "",
+ "gender": "MALE",
+ "moreInfo": "I am just a student :P",
+ "pictureKey": ""
+ }
+ }
+}
diff --git a/src/e2e/resources/data/InstructorStudentListPageE2ETest.json b/src/e2e/resources/data/InstructorStudentListPageE2ETest.json
new file mode 100644
index 00000000000..419ff10cce8
--- /dev/null
+++ b/src/e2e/resources/data/InstructorStudentListPageE2ETest.json
@@ -0,0 +1,179 @@
+{
+ "accounts": {
+ "instructorOfCourse1": {
+ "googleId": "ISListE2ET.idOfInstructor1OfCourse1",
+ "name": "Instructor of Course 1",
+ "isInstructor": true,
+ "email": "ISListE2E.instr1@course1.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "Student3Course3": {
+ "googleId": "ISListE2ET.charlie.tmms",
+ "name": "Charlie D",
+ "isInstructor": false,
+ "email": "ISListE2ET.charlie.tmms@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 5"
+ }
+ },
+ "courses": {
+ "course1": {
+ "id": "ISListE2ET.course1",
+ "name": "Name of Course 1",
+ "timeZone": "UTC"
+ },
+ "course2": {
+ "id": "ISListE2ET.course2",
+ "name": "Name of Course 2",
+ "timeZone": "UTC"
+ },
+ "course3": {
+ "id": "ISListE2ET.course3",
+ "name": "Name of Course 3",
+ "timeZone": "UTC"
+ }
+ },
+ "instructors": {
+ "instructorOfCourse1": {
+ "googleId": "ISListE2ET.idOfInstructor1OfCourse1",
+ "courseId": "ISListE2ET.course1",
+ "name": "Instructor1 Course1",
+ "email": "ISListE2E.instr1@course1.tmt",
+ "isArchived": false,
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructorOfCourse2": {
+ "googleId": "ISListE2ET.idOfInstructor1OfCourse1",
+ "courseId": "ISListE2ET.course2",
+ "name": "Instructor1 Course2",
+ "email": "ISListE2E.instr1@course1.tmt",
+ "isArchived": false,
+ "role": "Custom",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": false,
+ "cansubmitsessioninsection": false,
+ "canmodifysessioncommentinsection": false,
+ "canmodifycourse": false,
+ "canviewsessioninsection": false,
+ "canmodifysession": false,
+ "canmodifystudent": false,
+ "canmodifyinstructor": false
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "instructorOfCourse3": {
+ "googleId": "ISListE2ET.idOfInstructor1OfCourse1",
+ "courseId": "ISListE2ET.course3",
+ "name": "Instructor1 Course3",
+ "email": "ISListE2E.instr1@course1.tmt",
+ "isArchived": false,
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "Student1Course2": {
+ "googleId": "ISListE2ET.alice.tmms",
+ "email": "ISListE2ET.alice.tmms@gmail.tmt",
+ "course": "ISListE2ET.course2",
+ "name": "Alice",
+ "comments": "This student's name is Alice",
+ "team": "Team 1",
+ "section": "Section A"
+ },
+ "Student2Course2": {
+ "googleId": "ISListE2ET.benny.tmms",
+ "email": "benny.c.tmms@gmail.tmt",
+ "course": "ISListE2ET.course2",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 2",
+ "section": "Section B"
+ },
+ "Student3Course2": {
+ "googleId": "",
+ "email": "hugh.i.tmms@gmail.tmt",
+ "course": "ISListE2ET.course2",
+ "name": "Hugh Ivanov",
+ "comments": "This student's name is Hugh Ivanov",
+ "team": "Team 2",
+ "section": "Section B"
+ },
+ "Student1Course3": {
+ "googleId": "ISListE2ET.alice.tmms",
+ "email": "ISListE2ET.alice.tmms@gmail.tmt",
+ "course": "ISListE2ET.course3",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice",
+ "team": "Team 1",
+ "section": "Section 1"
+ },
+ "Student2Course3": {
+ "googleId": "",
+ "email": "benny.c.tmms@gmail.tmt",
+ "course": "ISListE2ET.course3",
+ "name": "Benny Charles",
+ "comments": "This student's name is Benny Charles",
+ "team": "Team 1",
+ "section": "Section 1"
+ },
+ "Student3Course3": {
+ "googleId": "ISListE2ET.charlie.tmms",
+ "email": "ISListE2ET.charlie.tmms@gmail.tmt",
+ "course": "ISListE2ET.course3",
+ "name": "Charlie D",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 2",
+ "section": "Section 1"
+ },
+ "Student4Course3": {
+ "googleId": "",
+ "email": "denny.c.tmms@gmail.tmt",
+ "course": "ISListE2ET.course3",
+ "name": "Denny Charlés",
+ "comments": "This student's name is Denny Charlés",
+ "team": "Team 2",
+ "section": "Section 1"
+ }
+ },
+ "feedbackSessions": {},
+ "feedbackQuestions": {},
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/data/InstructorStudentRecordsPageE2ETest.json b/src/e2e/resources/data/InstructorStudentRecordsPageE2ETest.json
new file mode 100644
index 00000000000..46d12f44d36
--- /dev/null
+++ b/src/e2e/resources/data/InstructorStudentRecordsPageE2ETest.json
@@ -0,0 +1,77 @@
+{
+ "accounts": {
+ "teammates.test": {
+ "googleId": "ISRE2eT.teammates.test",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "teammates.test@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "benny.c.tmms@ISR.CS2104": {
+ "googleId": "ISRE2eT.benny.c.tmms",
+ "name": "Benny Charlés",
+ "isInstructor": false,
+ "email": "benny.c.tmms@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 5"
+ }
+ },
+ "courses": {
+ "CS2104": {
+ "id": "ISRE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "UTC"
+ }
+ },
+ "instructors": {
+ "teammates.test.CS2104": {
+ "googleId": "ISRE2eT.teammates.test",
+ "courseId": "ISRE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "teammates.test@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "benny.c.tmms@ISR.CS2104": {
+ "googleId": "ISRE2eT.benny.c.tmms",
+ "email": "benny.c.tmms@gmail.tmt",
+ "course": "ISRE2eT.CS2104",
+ "name": "Benny Charlés",
+ "comments": "This student's name is Benny Charlés",
+ "team": "Team 1",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {},
+ "feedbackQuestions": {},
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {
+ "benny.c.tmms@ISR.CS2104": {
+ "googleId": "ISRE2eT.benny.c.tmms",
+ "shortName": "Ben",
+ "email": "",
+ "institute": "TEAMMATES Test Institute 7",
+ "nationality": "Singaporean",
+ "gender": "MALE",
+ "moreInfo": "This is a lot of info!",
+ "pictureKey": ""
+ }
+ }
+}
diff --git a/src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json b/src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json
index c10b0e8a019..99aba29374e 100644
--- a/src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json
+++ b/src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json
@@ -1,7 +1,7 @@
{
"accounts": {
- "SCDetailsUiT.instr": {
- "googleId": "SCDetailsUiT.instr",
+ "SCDetailsE2eT.instr": {
+ "googleId": "SCDetailsE2eT.instr",
"name": "Instructor",
"isInstructor": true,
"email": "tmms.test@gmail.tmt",
@@ -9,18 +9,42 @@
}
},
"courses": {
- "SCDetailsUiT.CS2104": {
- "id": "SCDetailsUiT.CS2104",
+ "SCDetailsE2eT.CS2104": {
+ "id": "SCDetailsE2eT.CS2104",
"name": "Programming Language Concepts",
- "timeZone": "UTC"
+ "timeZone": "UTC",
+ "createdAt": "2012-04-02T11:00:00Z"
}
},
"instructors": {
- "SCDetailsUiT.instr": {
- "googleId": "SCDetailsUiT.instr",
- "courseId": "SCDetailsUiT.CS2104",
+ "SCDetailsE2eT.instr": {
+ "googleId": "SCDetailsE2eT.instr",
+ "courseId": "SCDetailsE2eT.CS2104",
"name": "Teammates Test",
- "email": "SCDetailsUiT.instr@gmail.tmt",
+ "email": "SCDetailsE2eT.instr@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Instructor",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "SCDetailsE2eT.instr2": {
+ "googleId": "SCDetailsE2eT.instr2",
+ "courseId": "SCDetailsE2eT.CS2104",
+ "name": "Teammates Test 2",
+ "email": "SCDetailsE2eT2.instr@gmail.tmt",
"role": "Co-owner",
"isDisplayedToStudents": true,
"displayedName": "Instructor",
@@ -41,22 +65,31 @@
}
},
"students": {
- "SCDetailsUiT.alice": {
- "googleId": "SCDetailsUiT.alice.veryLongGoogleId",
- "email": "SCDetailsUiT.alice@gmail.tmt",
- "course": "SCDetailsUiT.CS2104",
- "name": "Alice Betsy'\"",
- "comments": "This student's name is Alice Betsy'\"",
- "team": "Team 1'\"",
+ "SCDetailsE2eT.alice": {
+ "googleId": "SCDetailsE2eT.alice",
+ "email": "SCDetailsE2eT.alice@gmail.tmt",
+ "course": "SCDetailsE2eT.CS2104",
+ "name": "Alice Betsy",
+ "comments": "This student's name is Alice Betsy",
+ "team": "Team 1",
"section": "None"
},
- "SCDetailsUiT.benny": {
- "googleId": "SCDetailsUiT.benny",
- "email": "SCDetailsUiT.benny@gmail.tmt",
- "course": "SCDetailsUiT.CS2104",
+ "SCDetailsE2eT.benny": {
+ "googleId": "SCDetailsE2eT.benny",
+ "email": "SCDetailsE2eT.benny@gmail.tmt",
+ "course": "SCDetailsE2eT.CS2104",
"name": "Benny Charles",
"comments": "This student's name is Benny Charles",
- "team": "Team 1'\"",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "SCDetailsE2eT.charlie": {
+ "googleId": "SCDetailsE2eT.charlie",
+ "email": "SCDetailsE2eT.charlie@gmail.tmt",
+ "course": "SCDetailsE2eT.CS2104",
+ "name": "Charlie Davis",
+ "comments": "This student's name is Charlie Davis",
+ "team": "Team 1",
"section": "None"
}
},
@@ -65,14 +98,24 @@
"feedbackResponses": {},
"feedbackResponseComments": {},
"profiles": {
- "SCDetailsUiT.student1InTSCourse": {
- "googleId": "SCDetailsUiT.benny",
+ "SCDetailsE2eT.benny": {
+ "googleId": "SCDetailsE2eT.benny",
"shortName": "Benny",
- "email": "SCDetailsUiT.benny@gmail.tmt",
- "institute": "inst",
+ "email": "SCDetailsE2eT.benny@gmail.tmt",
+ "institute": "inst",
"nationality": "American",
"gender": "OTHER",
- "moreInfo": "I am just a student :P",
+ "moreInfo": "I am just a student :P",
+ "pictureKey": ""
+ },
+ "SCDetailsE2eT.charlie": {
+ "googleId": "SCDetailsE2eT.charlie",
+ "shortName": "Charlie",
+ "email": "SCDetailsE2eT.charlie@gmail.tmt",
+ "institute": "inst",
+ "nationality": "Singaporean",
+ "gender": "OTHER",
+ "moreInfo": "I am also a student :P",
"pictureKey": ""
}
}
diff --git a/src/e2e/resources/data/StudentCourseJoinConfirmationPageE2ETest.json b/src/e2e/resources/data/StudentCourseJoinConfirmationPageE2ETest.json
new file mode 100644
index 00000000000..8874022ab53
--- /dev/null
+++ b/src/e2e/resources/data/StudentCourseJoinConfirmationPageE2ETest.json
@@ -0,0 +1,103 @@
+{
+ "accounts": {
+ "SCJConfirmationE2eT.instr": {
+ "googleId": "SCJConfirmationE2eT.instr",
+ "name": "Teammates Test",
+ "isInstructor": true,
+ "email": "SCJConfirmationE2eT.instr@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ },
+ "alice.tmms": {
+ "googleId": "SCJConfirmationE2eT.alice",
+ "name": "Alice B",
+ "isInstructor": false,
+ "email": "SCJConfirmationE2eT.alice@gmail.tmt",
+ "institute": "TEAMMATES Test Institute 1"
+ }
+ },
+ "courses": {
+ "SCJConfirmationE2eT.CS2104": {
+ "id": "SCJConfirmationE2eT.CS2104",
+ "name": "Programming Language Concepts",
+ "timeZone": "UTC"
+ },
+ "SCJConfirmationE2eT.CS1101": {
+ "id": "SCJConfirmationE2eT.CS1101",
+ "name": "Programming Methodology",
+ "timeZone": "UTC"
+ }
+ },
+ "instructors": {
+ "SCJConfirmationE2eT.instr.CS2104": {
+ "googleId": "SCJConfirmationE2eT.instr",
+ "courseId": "SCJConfirmationE2eT.CS2104",
+ "name": "Teammates Test",
+ "email": "SCJConfirmationE2eT.instr@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ },
+ "SCJConfirmationE2eT.instr.CS1101": {
+ "googleId": "SCJConfirmationE2eT.instr",
+ "courseId": "SCJConfirmationE2eT.CS1101",
+ "name": "Teammates Test",
+ "email": "SCJConfirmationE2eT.instr@gmail.tmt",
+ "role": "Co-owner",
+ "isDisplayedToStudents": true,
+ "displayedName": "Co-owner",
+ "privileges": {
+ "courseLevel": {
+ "canviewstudentinsection": true,
+ "cansubmitsessioninsection": true,
+ "canmodifysessioncommentinsection": true,
+ "canmodifycourse": true,
+ "canviewsessioninsection": true,
+ "canmodifysession": true,
+ "canmodifystudent": true,
+ "canmodifyinstructor": true
+ },
+ "sectionLevel": {},
+ "sessionLevel": {}
+ }
+ }
+ },
+ "students": {
+ "alice.tmms@SCJConfirmationE2eT.CS2104": {
+ "googleId": "",
+ "email": "SCJConfirmationE2eT.alice@gmail.tmt",
+ "course": "SCJConfirmationE2eT.CS2104",
+ "name": "Amy Betsy",
+ "comments": "This student's name is Amy Betsy",
+ "team": "Team 1",
+ "section": "None"
+ },
+ "alice.tmms@SCJConfirmationE2eT.CS1101": {
+ "googleId": "SCJConfirmationE2eT.alice",
+ "email": "SCJConfirmationE2eT.alice@gmail.tmt",
+ "course": "SCJConfirmationE2eT.CS1101",
+ "name": "Amy Betsy",
+ "comments": "This student's name is Amy Betsy",
+ "team": "Team 1",
+ "section": "None"
+ }
+ },
+ "feedbackSessions": {},
+ "feedbackQuestions": {},
+ "feedbackResponses": {},
+ "feedbackResponseComments": {},
+ "profiles": {}
+}
diff --git a/src/e2e/resources/test.template.properties b/src/e2e/resources/test.template.properties
index 2207377e331..f2e380bcae6 100644
--- a/src/e2e/resources/test.template.properties
+++ b/src/e2e/resources/test.template.properties
@@ -6,7 +6,7 @@
# This is the url of the app we are testing against.
# e.g. test.app.url=http\://localhost\:8080
-# e.g. test.app.url=https\://6-0-0-dot-teammates-john.appspot.com
+# e.g. test.app.url=https\://7-0-0-dot-teammates-john.appspot.com
# Note: the '.' in the url has been replaced by -dot- to support https connection for the staging server.
test.app.url=http\://localhost\:8080
@@ -24,6 +24,10 @@ test.backdoor.key=samplekey
# allowed values for browser: firefox, chrome
test.selenium.browser=firefox
+# Indicates whether browser should be closed when an E2E test fails.
+# Browser is always closed after an E2E test succeeds.
+test.browser.closeonfailure=true
+
# Optional field to change Firefox path.
# This allows using a non-default binary.
# To use the default Firefox in your PATH variable, leave the field empty.
@@ -43,6 +47,18 @@ test.geckodriver.path=
# e.g test.chromedriver.path=/Users/YourName/Downloads/chromedriver
test.chromedriver.path=
+# This is the profile for Firefox used for logging in.
+# It is required if you are running test with Firefox and not using dev server.
+# Enter about:profiles into Firefox address bar to identify the profile you are using.
+# e.g test.firefox.profile.name=default-release
+test.firefox.profile.name=
+
+# This is the path for Chrome's user data used for logging in.
+# It is required if you are running test with Chrome and not using dev server.
+# e.g test.chromedriver.path=C:/Users/YourName/AppData/Local/Google/User Data
+# e.g test.chromedriver.path=/Users/YourName/Library/Application Support/Google/Chrome/Default
+test.chrome.userdata.path=
+
###############################################################################
# This is the timeout value, in seconds, for the waitings done in browsers
# e.g waiting for element (non-)presence/(in)visibility, for page to load
diff --git a/src/e2e/resources/test.travis-chrome.properties b/src/e2e/resources/test.travis-chrome.properties
index af40a38a5cb..10f85e22b39 100644
--- a/src/e2e/resources/test.travis-chrome.properties
+++ b/src/e2e/resources/test.travis-chrome.properties
@@ -6,8 +6,9 @@ test.app.url=http\://localhost\:8080
test.csrf.key=samplekey
test.backdoor.key=samplekey
test.selenium.browser=chrome
+test.browser.closeonfailure=true
test.chromedriver.path=/home/travis/chromedriver
-test.timeout=15
+test.timeout=5
test.persistence.timeout=16
test.admin.account=yourGoogleId
test.admin.password=adminpassword
diff --git a/src/e2e/resources/test.travis.properties b/src/e2e/resources/test.travis.properties
index ef11322892a..8b9a268a784 100644
--- a/src/e2e/resources/test.travis.properties
+++ b/src/e2e/resources/test.travis.properties
@@ -6,9 +6,10 @@ test.app.url=http\://localhost\:8080
test.csrf.key=samplekey
test.backdoor.key=samplekey
test.selenium.browser=firefox
+test.browser.closeonfailure=true
test.geckodriver.path=/home/travis/geckodriver
test.firefox.path=
-test.timeout=15
+test.timeout=5
test.persistence.timeout=16
test.admin.account=yourGoogleId
test.admin.password=adminpassword
diff --git a/src/e2e/resources/testng-e2e.xml b/src/e2e/resources/testng-e2e.xml
index f928d59c83d..0a7c6206b6e 100644
--- a/src/e2e/resources/testng-e2e.xml
+++ b/src/e2e/resources/testng-e2e.xml
@@ -9,16 +9,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/src/main/java/teammates/common/datatransfer/CommonAccountSearchResult.java b/src/main/java/teammates/common/datatransfer/CommonAccountSearchResult.java
deleted file mode 100644
index 5ab2c8997c5..00000000000
--- a/src/main/java/teammates/common/datatransfer/CommonAccountSearchResult.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package teammates.common.datatransfer;
-
-/**
- * Represents common details of an account.
- * *
Contains:
- * *
* account name, email, google id, course name, institution.
- * *
* link for join course, home page and manage account.
- */
-public class CommonAccountSearchResult {
- protected String name;
- protected String email;
- protected String googleId;
- protected String courseId;
- protected String courseName;
- protected String institute;
-
- protected String courseJoinLink;
- protected String homePageLink;
- protected String manageAccountLink;
- protected boolean showLinks;
-
- public void setName(String name) {
- this.name = name;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public void setGoogleId(String googleId) {
- this.googleId = googleId;
- }
-
- public void setCourseId(String courseId) {
- this.courseId = courseId;
- }
-
- public void setCourseJoinLink(String courseJoinLink) {
- this.courseJoinLink = courseJoinLink;
- }
-
- public void setCourseName(String courseName) {
- this.courseName = courseName;
- }
-
- public void setHomePageLink(String homePageLink) {
- this.homePageLink = homePageLink;
- }
-
- public void setInstitute(String institute) {
- this.institute = institute;
- }
-
- public void setManageAccountLink(String manageAccountLink) {
- this.manageAccountLink = manageAccountLink;
- }
-
- public String getName() {
- return name;
- }
-
- public String getEmail() {
- return email;
- }
-
- public String getGoogleId() {
- return googleId;
- }
-
- public String getCourseId() {
- return courseId;
- }
-
- public String getCourseName() {
- return courseName;
- }
-
- public String getInstitute() {
- return institute;
- }
-
- public String getCourseJoinLink() {
- return courseJoinLink;
- }
-
- public String getHomePageLink() {
- return homePageLink;
- }
-
- public String getManageAccountLink() {
- return manageAccountLink;
- }
-}
diff --git a/src/main/java/teammates/common/datatransfer/CourseDetailsBundle.java b/src/main/java/teammates/common/datatransfer/CourseDetailsBundle.java
deleted file mode 100644
index ee04940f9f4..00000000000
--- a/src/main/java/teammates/common/datatransfer/CourseDetailsBundle.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package teammates.common.datatransfer;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-
-import teammates.common.datatransfer.attributes.CourseAttributes;
-import teammates.common.datatransfer.attributes.FeedbackSessionAttributes;
-import teammates.common.util.SanitizationHelper;
-
-/**
- * Represents details of a course, including its students and feedback sessions.
- *
Contains:
- *
* statistics of teams, enrollments, registrations
- *
* Details of its feedback sessions (as {@link FeedbackSessionDetailsBundle} objects)
- *
* Details of its teams (as {@link TeamDetailsBundle} objects)
- *
- */
-public class CourseDetailsBundle {
- public CourseAttributes course;
- public CourseStats stats = new CourseStats();
-
- public List feedbackSessions = new ArrayList<>();
- public List sections = new ArrayList<>();
-
- public CourseDetailsBundle(CourseAttributes courseData) {
- this.course = courseData;
- //TODO: [CourseAttribute] remove desanitization after data migration
- //creating a new course with possibly desanitized name as course name cannot be accessed directly
- this.course = CourseAttributes
- .builder(courseData.getId())
- .withName(SanitizationHelper.desanitizeIfHtmlSanitized(courseData.getName()))
- .withTimezone(courseData.getTimeZone())
- .build();
- this.course.createdAt = courseData.createdAt;
- }
-
- /**
- * Gets all FeedbackSessionAttributes in this CourseDetailsBundle.
- */
- public List getFeedbackSessionsList() {
- List feedbackSessionAttributes = new ArrayList<>();
- for (FeedbackSessionDetailsBundle feedbackSessionDetails : feedbackSessions) {
- feedbackSessionAttributes.add(feedbackSessionDetails.feedbackSession);
- }
- return feedbackSessionAttributes;
- }
-
- /**
- * Sorts courses based on course ID.
- */
- public static void sortDetailedCoursesByCourseId(List courses) {
- courses.sort(Comparator.comparing(obj -> obj.course.getId()));
- }
-
- /**
- * Sorts courses based on course creation date in the order of latest to oldest order.
- */
- public static void sortDetailedCoursesByCreationDate(List courses) {
- courses.sort(Comparator.comparing((CourseDetailsBundle obj) -> obj.course.createdAt).reversed());
- }
-
- public CourseStats getStats() {
- return stats;
- }
-
- public CourseAttributes getCourse() {
- return course;
- }
-}
diff --git a/src/main/java/teammates/common/datatransfer/CourseRoster.java b/src/main/java/teammates/common/datatransfer/CourseRoster.java
index b5529c55fdd..6e88c9aad6c 100644
--- a/src/main/java/teammates/common/datatransfer/CourseRoster.java
+++ b/src/main/java/teammates/common/datatransfer/CourseRoster.java
@@ -39,15 +39,6 @@ public Map> getTeamToMembersTable() {
return teamToMembersTable;
}
- /**
- * Checks if an instructor is the instructor of a course by providing an email address.
- * @param instructorEmail email of the instructor to be checked.
- * @return true if the instructor is an instructor of the course
- */
- public boolean isInstructorOfCourse(String instructorEmail) {
- return instructorListByEmail.containsKey(instructorEmail);
- }
-
public boolean isStudentInCourse(String studentEmail) {
return studentListByEmail.containsKey(studentEmail);
}
@@ -79,25 +70,6 @@ public InstructorAttributes getInstructorForEmail(String email) {
return instructorListByEmail.get(email);
}
- /**
- * Returns a map of email mapped to name of instructors and students of the course.
- *
- * @return Map in which key is email of student/instructor and value is name.
- */
- public Map