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

E2E test on Firefox #141

Merged
merged 4 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

This folder contains E2E test cases of Copy as Markdown extension.

It uses [Selenium](https://www.selenium.dev/) to automate the browsers and assert the contents of the clipboard.
It uses [Selenium](https://www.selenium.dev/) and [java.awt.Robot](https://docs.oracle.com/javase%2F7%2Fdocs%2Fapi%2F%2F/java/awt/Robot.html) to automate the browsers and assert the contents of the clipboard.

## System Requirements

* Java
* Maven
* Google Chrome
* Firefox

## Development

Expand All @@ -22,7 +23,7 @@ It uses [Selenium](https://www.selenium.dev/) to automate the browsers and asser

## Setup

### macOS Assistive Control
### macOS Accessibility Warnings

When you first run the test cases in the terminal, you will be prompted about
the permissions of Assistive Control.
Expand All @@ -41,17 +42,18 @@ Please hold back and don't touch mouse / keyboard while the tests are running.

### JetBrains Aqua

Just right-click on the test folder and choose "Run Tests".
- Right-click on `testng.xml` and choose "Run Tests".
- To run test cases for a particular browser, select `testng-<browser>.xml`.

## Architecture

* `src/test` contains all the test scripts.
* `support/e2e-test-extension` is a Web Extension used to control tabs in ways that Selenium can't do,
* `support/e2e-test-extension*` are Web Extensions used to control tabs in ways that Selenium can't do,
such as tab grouping, tab highlighting etc.
* `support/pages` contains static fixture pages used in test cases. When test suite starts, it will run a static server
listening at `localhost:5566`. The E2E Test Extension will open those pages.

## TODO

* Test on Firefox
* Test on Linux
* Test Edge
* Test on Linux & Windows
21 changes: 10 additions & 11 deletions e2e/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<junit.version>5.10.0</junit.version>
<aspectj.version>1.9.19</aspectj.version>
<allure.version>2.24.0</allure.version>
</properties>
Expand All @@ -27,20 +26,14 @@
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit5</artifactId>
<artifactId>allure-testng</artifactId>
<version>${allure.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.7.1</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -61,6 +54,12 @@
<version>3.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.7.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
156 changes: 121 additions & 35 deletions e2e/src/test/java/org/yorkxin/copyasmarkdown/e2e/BaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

import com.sun.net.httpserver.HttpServer;
import io.github.sukgu.Shadow;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.interactions.Actions;
import org.testng.annotations.*;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
Expand All @@ -23,32 +24,29 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.sun.net.httpserver.SimpleFileServer.createFileHandler;

record Window(String handle, String url, String title) {}

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BaseTest {
protected String browser;
protected WebDriver driver;
private HttpServer server;
protected String extId;
protected String e2eExtId;
protected String mainWindowHandle;
protected String demoWindowHandle;
protected Clipboard clipboard;
List<Window> windows = new ArrayList<>();

@BeforeAll
public void setUp() throws IOException, InterruptedException, AWTException {
ChromeOptions options = new ChromeOptions();
// Fix the issue https://github.com/SeleniumHQ/selenium/issues/11750
options.addArguments("--remote-allow-origins=*");
options.addArguments("--load-extension=../chrome,./support/e2e-test-extension");
driver = new ChromeDriver(options);
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));

protected final static String BROWSER_CHROME = "chrome";
protected final static String BROWSER_FIREFOX = "firefox";

@Parameters("browser")
@BeforeClass
public void setUp(@Optional(BROWSER_CHROME) String browserName) throws IOException {
browser = browserName;
driver = getDriver(browser);
clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

extId = findExtension("Copy as Markdown");
Expand All @@ -63,12 +61,53 @@ public void setUp() throws IOException, InterruptedException, AWTException {
server.start();
System.out.printf("started serving on %s\n", server.getAddress());

driver.get("chrome-extension://"+e2eExtId+"/main.html?base_url=http://localhost:5566");
openE2eExtensionMainPage();
mainWindowHandle = driver.getWindowHandle();
}

private WebDriver getDriver(String browser) {
WebDriver wd;
switch (browser) {
case BROWSER_CHROME:
ChromeOptions co = new ChromeOptions();
// Fix the issue https://github.com/SeleniumHQ/selenium/issues/11750
co.addArguments("--remote-allow-origins=*");
co.addArguments("--load-extension=../chrome,./support/e2e-test-extension");
wd = new ChromeDriver(co);
break;
case BROWSER_FIREFOX:
FirefoxProfile profile = new FirefoxProfile();
profile.setPreference("intl.locale.requested","en-us");
FirefoxOptions fo = new FirefoxOptions();
fo.setProfile(profile);
FirefoxDriver fd = new FirefoxDriver(fo);
fd.installExtension(Path.of("../firefox"), true);
fd.installExtension(Path.of("./support/firefox-e2e-test-extension"), true);
wd = fd;
break;

default:
throw new IllegalArgumentException("unsupported browser: "+browser);
}

wd.manage().window().maximize();
wd.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
return wd;
}

private String findExtension(String extensionName) {
// get extension ID
String id = "";
switch (browser) {
case BROWSER_CHROME -> id = findExtensionInChrome(extensionName);
case BROWSER_FIREFOX -> id = findExtensionInFirefox(extensionName);
default -> throw new IllegalArgumentException("unsupported browser: "+browser);
}
return id;
}

private String findExtensionInChrome(String extensionName) {
assert browser.equals(BROWSER_CHROME);

driver.get("chrome://extensions/");

WebElement myExtension = null;
Expand All @@ -84,33 +123,57 @@ private String findExtension(String extensionName) {
}

if (myExtension == null) {
throw new IllegalArgumentException("extension not found");
throw new IllegalArgumentException("extension not found: "+ extensionName);
}

return myExtension.getAttribute("id");
}

@BeforeEach
private String findExtensionInFirefox(String extensionName) {
assert browser.equals(BROWSER_FIREFOX);

driver.get("about:debugging#/runtime/this-firefox");

WebElement myExtension = null;
List<WebElement> extensions = driver.findElements(By.className("debug-target-item"));

for (WebElement ext : extensions) {
String name = ext.findElement(By.className("debug-target-item__name")).getText() ;
if (Objects.equals(name, extensionName)){
myExtension = ext;
break;
}
}

if (myExtension == null) {
throw new IllegalArgumentException("extension not found: "+ extensionName);
}

WebElement manifestLink = myExtension.findElement(By.xpath(".//a[contains(@href,\"moz-extension\")]"));
if (manifestLink == null) {
throw new RuntimeException("could not find extension ID by looking for a link to manifest.json");
}

Pattern pattern = Pattern.compile("^moz-extension://([A-Za-z0-9\\-]+)/.+$");
Matcher matcher = pattern.matcher(manifestLink.getAttribute("href"));
if (!matcher.matches()) {
throw new RuntimeException("could not find extension ID by matching the link to manifest.json");
}

return matcher.toMatchResult().group(1);
}

@BeforeMethod
public void resetClipboard() {
clipboard.setContents(new StringSelection("========TEST SEPARATOR========"),null);
}

@AfterAll
@AfterClass
public void tearDown() {
driver.quit();
server.stop(0);
}

protected String findWindow(String title) {
for (Window w: windows) {
System.out.print(w);
if (Objects.equals(w.title(), title)) {
return w.handle();
}
}
return null;
}

protected void selectAll() {
Keys cmdCtrl = Platform.getCurrent().is(Platform.MAC) ? Keys.COMMAND : Keys.CONTROL;
Actions actions = new Actions(driver);
Expand All @@ -121,10 +184,33 @@ protected void selectAll() {
.perform();
}

protected void openDemoTabs() {
private void openE2eExtensionMainPage() {
driver.get(getExtensionProtocol()+"://"+e2eExtId+"/main.html?base_url=http://localhost:5566");
}

protected String getExtensionProtocol() {
return switch (browser) {
case BROWSER_CHROME -> "chrome-extension";
case BROWSER_FIREFOX -> "moz-extension";
default -> throw new IllegalStateException("Unexpected value: " + browser);
};
}

protected DemoPageData openDemoTabs(boolean groupTabs) {
driver.switchTo().window(mainWindowHandle);
openE2eExtensionMainPage();
driver.findElement(By.id("open-demo")).click();
driver.switchTo().window(mainWindowHandle);

// order matters - must group tabs first then highlight tabs,
// otherwise a new group will de-highlight tabs inside it.
if (groupTabs) {
driver.findElement(By.id("group-tabs")).click();
}
driver.findElement(By.id("highlight-tabs")).click();

String demoWindowId = driver.findElement(By.id("window-id")).getAttribute("value");
String tab0Id = driver.findElement(By.id("tab-0-id")).getAttribute("value");
return new DemoPageData(demoWindowId, tab0Id);
}
}
Loading
Loading