Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Production server e2e #7

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
14 changes: 7 additions & 7 deletions docs/e2e-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Before running tests, modify `src/e2e/resources/test.properties` if necessary, e

* If you are planning to test against a production server, specify the path to Chrome's user data directory in `test.chrome.userdata.path` value in `test.properties`.
* This is used to bypass login by using previous login data.
* You can enter `chrome://version` into Chrome address bar to identify the profile path (specified under `Profile Path`).

* The chromedriver process started by the test suite will not automatically get killed after the tests have finished executing.<br>
You will need to manually kill these processes after the tests are done.
Expand All @@ -71,23 +72,22 @@ Any individual E2E test | `./gradlew e2eTestTry1 --tests TestClassName` | `{proj

If you are testing against a production server (staging server or live server), some additional tasks need to be done.

1. Edit `src/e2e/resources/test.properties` as instructed is in its comments.
* In particular, you will need a legitimate Gmail account to be used for testing.

1. You need to setup a `Gmail API`<sup>1</sup> as follows:
* [Obtain a Gmail API credentials](https://github.com/TEAMMATES/teammates-ops/blob/master/platform-guide.md) and download it.
* [Obtain a Gmail API credentials](https://github.com/TEAMMATES/teammates-ops/blob/master/platform-guide.md#setting-up-gmail-api-credentials) and download it.
* Copy the file to `src/e2e/resources/gmail-api` (create the `gmail-api` folder) of your project and rename it to `client_secret.json`.
* It is also possible to use the Gmail API credentials from any other Google Cloud Platform project for this purpose.

1. Edit `src/e2e/resources/test.properties` as instructed is in its comments.
* In particular, you will need a legitimate Gmail account to be used for testing.
* Run `EmailAccountTest` to confirm that the setup works. For the first run, it is expected that you will need to grant access from the test Gmail account to the above API.

1. Login manually to TEAMMATES on the browser used for testing to add cookie with login details to the browser profile.
* This profile will be added to the web driver so that E2E tests will start with user already logged in.
* This is required as Google does not allow login by automated software.

1. For Firefox, run the full test suite or any subset of it as how you would have done it in dev server.
1. Run the full test suite or any subset of it as how you would have done it in dev server.
* Do note that the GAE daily quota is usually not enough to run the full test suite, in particular for accounts with no billing enabled.

1. For Chrome, you may have to run tests one at a time as multiple ChromeDriver instances cannot be opened with the same user data.

<sup>1</sup> This setup is necessary because our test suite uses the Gmail API to access the Gmail account used for testing (the account is specified in `test.properties`) to confirm that the account receives the expected emails from TEAMMATES.
This is needed only when testing against a production server because no actual emails are sent by the dev server and therefore delivery of emails is not tested when testing against the dev server.

Expand Down
7 changes: 5 additions & 2 deletions src/e2e/java/teammates/e2e/cases/AdminSearchPageE2ETest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import teammates.common.util.Const;
import teammates.common.util.StringHelper;
import teammates.e2e.pageobjects.AdminSearchPage;
import teammates.e2e.util.TestProperties;

/**
* SUT: {@link Const.WebPageURIs#ADMIN_SEARCH_PAGE}.
Expand Down Expand Up @@ -237,8 +238,10 @@ private void verifyLinkExpansionButtons(StudentAttributes student, InstructorAtt
}

private void verifyRegenerateStudentCourseLinks(WebElement studentRow, String originalJoinLink) {
searchPage.verifyStatusMessage("Student's links for this course have been successfully regenerated,"
+ " and the email has been sent.");
if (TestProperties.isDevServer() || TestProperties.INCLUDE_EMAIL_VERIFICATION) {
searchPage.verifyStatusMessage("Student's links for this course have been successfully "
+ "regenerated, and the email has been sent.");
}

String regeneratedJoinLink = searchPage.getStudentJoinLink(studentRow);
assertNotEquals(regeneratedJoinLink, originalJoinLink);
Expand Down
31 changes: 26 additions & 5 deletions src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.testng.ITestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeSuite;

import teammates.common.datatransfer.DataBundle;
import teammates.common.datatransfer.attributes.AccountAttributes;
Expand Down Expand Up @@ -44,18 +45,36 @@
public abstract class BaseE2ETestCase extends BaseTestCaseWithDatastoreAccess {

static final BackDoor BACKDOOR = BackDoor.getInstance();
private static Browser sharedBrowser;

protected Browser browser;
protected DataBundle testData;

@BeforeSuite
protected void determineEnvironment(ITestContext context) {
if (!TestProperties.isDevServer()) {
// If testing against production server, run in single thread only
context.getSuite().getXmlSuite().setThreadCount(1);
}
}

@BeforeClass
public void baseClassSetup() throws Exception {
prepareTestData();
prepareBrowser();
}

protected void prepareBrowser() {
browser = new Browser();
if (TestProperties.isDevServer()) {
browser = new Browser();
} else {
// As the tests are run in single thread, in order to reduce the time wasted on browser setup/teardown,
// use a single browser instance for all tests in the suite
if (sharedBrowser == null) {
sharedBrowser = new Browser();
}
browser = sharedBrowser;
}
}

protected abstract void prepareTestData() throws Exception;
Expand Down Expand Up @@ -83,6 +102,9 @@ protected void releaseBrowser(boolean isSuccess) {
if (browser == null) {
return;
}
if (!TestProperties.isDevServer()) {
return;
}
if (isSuccess || TestProperties.CLOSE_BROWSER_ON_FAILURE) {
browser.driver.close();
}
Expand Down Expand Up @@ -189,7 +211,7 @@ protected void verifyDownloadedFile(String expectedFileName, List<String> expect
* Email used must be an authentic gmail account.
*/
protected void verifyEmailSent(String email, String subject) {
if (TestProperties.isDevServer()) {
if (TestProperties.isDevServer() || !TestProperties.INCLUDE_EMAIL_VERIFICATION) {
return;
}
if (!TestProperties.TEST_EMAIL.equals(email)) {
Expand All @@ -199,13 +221,12 @@ protected void verifyEmailSent(String email, String subject) {
try {
emailAccount.getUserAuthenticated();
int retryLimit = 5;
boolean actual = emailAccount.isEmailWithSubjectPresent(subject);
boolean actual = emailAccount.isRecentEmailWithSubjectPresent(subject, TestProperties.TEST_SENDER_EMAIL);
while (!actual && retryLimit > 0) {
retryLimit--;
ThreadHelper.waitFor(1000);
actual = emailAccount.isEmailWithSubjectPresent(subject);
actual = emailAccount.isRecentEmailWithSubjectPresent(subject, TestProperties.TEST_SENDER_EMAIL);
}
emailAccount.markAllUnreadEmailAsRead();
assertTrue(actual);
} catch (Exception e) {
fail("Failed to verify email sent:" + e);
Expand Down
14 changes: 4 additions & 10 deletions src/e2e/java/teammates/e2e/cases/BaseFeedbackQuestionE2ETest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,20 @@ protected InstructorFeedbackEditPage loginToFeedbackEditPage() {
}

protected FeedbackSubmitPage loginToFeedbackSubmitPage() {
AppUrl url = createUrl(Const.WebPageURIs.SESSION_SUBMISSION_PAGE)
AppUrl url = createUrl(Const.WebPageURIs.STUDENT_SESSION_SUBMISSION_PAGE)
.withUserId(student.googleId)
.withCourseId(student.course)
.withSessionName(feedbackSession.getFeedbackSessionName());

FeedbackSubmitPage submitPage = loginAdminToPage(url, FeedbackSubmitPage.class);
submitPage.reloadPageIfStuckLoading();

return submitPage;
return loginAdminToPage(url, FeedbackSubmitPage.class);
}

protected FeedbackSubmitPage getFeedbackSubmitPage() {
AppUrl url = createUrl(Const.WebPageURIs.SESSION_SUBMISSION_PAGE)
AppUrl url = createUrl(Const.WebPageURIs.STUDENT_SESSION_SUBMISSION_PAGE)
.withUserId(student.googleId)
.withCourseId(student.course)
.withSessionName(feedbackSession.getFeedbackSessionName());

FeedbackSubmitPage submitPage = AppPage.getNewPageInstance(browser, url, FeedbackSubmitPage.class);
submitPage.reloadPageIfStuckLoading();

return submitPage;
return AppPage.getNewPageInstance(browser, url, FeedbackSubmitPage.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public void testAll() {
}

private AppUrl getStudentSubmitPageUrl(StudentAttributes student, FeedbackSessionAttributes session) {
return createUrl(Const.WebPageURIs.SESSION_SUBMISSION_PAGE)
return createUrl(Const.WebPageURIs.STUDENT_SESSION_SUBMISSION_PAGE)
.withUserId(student.googleId)
.withCourseId(student.course)
.withSessionName(session.getFeedbackSessionName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public void testAll() {

detailsPage.verifyStatusMessage("An email has been sent to " + student.getEmail());
String expectedEmailSubject = "TEAMMATES: Invitation to join course ["
+ course.getName() + "][" + course.getId() + "]";
+ course.getName() + "][Course ID: " + course.getId() + "]";
verifyEmailSent(student.getEmail(), expectedEmailSubject);

______TS("remind all students to join");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ public void testAll() {
student.googleId = null;
editPage.editStudentEmailAndResendLinks(newEmail);

editPage.verifyStatusMessage("Student has been updated and email sent");
if (TestProperties.isDevServer() || TestProperties.INCLUDE_EMAIL_VERIFICATION) {
editPage.verifyStatusMessage("Student has been updated and email sent");
}
verifyPresentInDatastore(student);
verifyEmailSent(newEmail, "TEAMMATES: Summary of course ["
+ course.getName() + "][Course ID: " + course.getId() + "]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public void testAll() {

______TS("search for valid student");
InstructorSearchPage searchPage = homePage.searchKeyword(studentToEmail.getName());
searchPage.waitForPageToLoad();

// Here, it is sufficient to ensure that the number of search results matches
// A more thorough testing of this page will be done in its own E2E test
Expand All @@ -82,6 +83,7 @@ public void testAll() {

______TS("search for invalid student");
searchPage = homePage.searchKeyword("INVALID");
searchPage.waitForPageToLoad(true);

searchPage.verifyStatusMessage("No results found.");
searchPage.verifyNumCoursesInStudentResults(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ public void testAll() {
.withStudentEmail(unregistered.email)
.withSessionName(openSession.getFeedbackSessionName())
.withRegistrationKey(getKeyForStudent(unregistered));
logout();
resultsPage = AppPage.getNewPageInstance(browser, url, StudentFeedbackResultsPage.class);

resultsPage.verifyFeedbackSessionDetails(openSession);
Expand Down
2 changes: 0 additions & 2 deletions src/e2e/java/teammates/e2e/cases/StudentHomePageE2ETest.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ public void testAll() {

assertTrue(verifyVisibleFeedbackSessionToStudents(feedbackSessionName, i));
}

logout();
}

private List<WebElement> getStudentHomeCoursePanels() {
Expand Down
2 changes: 0 additions & 2 deletions src/e2e/java/teammates/e2e/cases/TimezoneSyncerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ public void testAll() {
releaseDate.plusDays(DAYS_TO_UPDATE_TZ).isAfter(nowDate));

}

logout();
}

private String processOffsets(String offsets) {
Expand Down
13 changes: 6 additions & 7 deletions src/e2e/java/teammates/e2e/pageobjects/AdminSearchPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ public void clickSearchButton() {
public void regenerateLinksForStudent(StudentAttributes student) {
WebElement studentRow = getStudentRow(student);
studentRow.findElement(By.xpath("//button[text()='Regenerate links']")).click();
waitForPageToLoad();

waitForConfirmationModalAndClickOk();
waitForPageToLoad(true);
Expand Down Expand Up @@ -153,11 +152,11 @@ public String getStudentJoinLink(WebElement studentRow) {

public void resetStudentGoogleId(StudentAttributes student) {
WebElement studentRow = getStudentRow(student);
studentRow.findElement(By.linkText(LINK_TEXT_RESET_GOOGLE_ID)).click();
waitForPageToLoad();
WebElement link = studentRow.findElement(By.linkText(LINK_TEXT_RESET_GOOGLE_ID));
link.click();

waitForConfirmationModalAndClickOk();
waitForPageToLoad();
waitForElementStaleness(link);
}

public WebElement getInstructorRow(InstructorAttributes instructor) {
Expand Down Expand Up @@ -201,11 +200,11 @@ public String getInstructorJoinLink(WebElement instructorRow) {

public void resetInstructorGoogleId(InstructorAttributes instructor) {
WebElement instructorRow = getInstructorRow(instructor);
instructorRow.findElement(By.linkText(LINK_TEXT_RESET_GOOGLE_ID)).click();
waitForPageToLoad();
WebElement link = instructorRow.findElement(By.linkText(LINK_TEXT_RESET_GOOGLE_ID));
link.click();

waitForConfirmationModalAndClickOk();
waitForPageToLoad();
waitForElementStaleness(link);
}

public int getNumExpandedRows(WebElement row) {
Expand Down
28 changes: 6 additions & 22 deletions src/e2e/java/teammates/e2e/pageobjects/AppPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.util.Map;

import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.InvalidElementStateException;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
Expand Down Expand Up @@ -183,9 +182,13 @@ public void waitForElementToBeClickable(WebElement element) {
}

public void waitUntilAnimationFinish() {
WebDriverWait wait = new WebDriverWait(browser.driver, 2);
WebDriverWait wait = new WebDriverWait(browser.driver, 3);
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.className("ng-animating")));
ThreadHelper.waitFor(500);
ThreadHelper.waitFor(1000);
}

public void waitForLoadingElement() {
waitForElementStaleness(waitForElementPresence(By.className("loading-container")));
}

/**
Expand Down Expand Up @@ -251,17 +254,6 @@ public void reloadPage() {
waitForPageToLoad();
}

public void reloadPageIfStuckLoading() {
By loadingContainer = By.className("loading-container");
try {
if (isElementPresent(loadingContainer)) {
waitForElementStaleness(browser.driver.findElement(loadingContainer));
}
} catch (TimeoutException e) {
reloadPage();
}
}

protected Object executeScript(String script, Object... args) {
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) browser.driver;
return javascriptExecutor.executeScript(script, args);
Expand Down Expand Up @@ -653,14 +645,6 @@ public void run() {
}
}

/**
* Set browser window to x width and y height.
*/
protected void setWindowSize(int x, int y) {
Dimension d = new Dimension(x, y);
browser.driver.manage().window().setSize(d);
}

/**
* Switches to the new browser window just opened.
*/
Expand Down
7 changes: 5 additions & 2 deletions src/e2e/java/teammates/e2e/pageobjects/Browser.java
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ private WebDriver createWebDriver() {
FirefoxProfile profile;
if (TestProperties.isDevServer()) {
profile = new FirefoxProfile();
profile.setPreference("browser.private.browsing.autostart", true);
} 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.
Expand All @@ -165,6 +164,10 @@ private WebDriver createWebDriver() {
profile.setPreference("browser.download.dir", downloadPath);

FirefoxOptions options = new FirefoxOptions().setProfile(profile);
if (TestProperties.isDevServer()) {
options.addArguments("-private");
}

return new FirefoxDriver(options);
}

Expand All @@ -174,7 +177,7 @@ private WebDriver createWebDriver() {

Map<String, Object> chromePrefs = new HashMap<>();
chromePrefs.put("download.default_directory", downloadPath);
chromePrefs.put("profile.default_content_settings.popups", 0);
chromePrefs.put("download.prompt_for_download", false);
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("prefs", chromePrefs);
options.addArguments("--allow-file-access-from-files");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -721,13 +721,13 @@ private int getRecipientIndex(int qnNumber, String recipient) {
} catch (NoSuchElementException e) {
// continue
}
int i = 0;
while (true) {
int limit = 20; // we are not likely to set test data exceeding this number
for (int i = 0; i < limit; i++) {
if (questionForm.findElement(By.id("recipient-name-" + i)).getText().contains(recipient)) {
return i;
}
i++;
}
return -1;
}

private WebElement getTextResponseEditor(int qnNumber, String recipient) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ private void clickAddNewInstructorButton() {

private void clickEditInstructorButton(int instrNum) {
click(getEditInstructorButton(instrNum));
waitUntilAnimationFinish();
}

private void clickCancelInstructorButton(int instrNum) {
Expand Down
Loading