Skip to content

Commit

Permalink
Merge branch 'release/0.14.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
nwtgck committed Jan 3, 2023
2 parents 932ea1b + 3fef88c commit 5f4f5d2
Show file tree
Hide file tree
Showing 35 changed files with 497 additions and 394 deletions.
15 changes: 14 additions & 1 deletion .github/workflows/e2e-test.yml
Expand Up @@ -21,13 +21,26 @@ jobs:
# "selenium/standalone-${e2e_docker_image_fragment}"
# Why fragment? These short names are more readable in GitHub UI
- e2e_docker_image_fragment: chrome:108.0
e2e_service_worker_disable: false
e2e_block_popup: false
- e2e_docker_image_fragment: chrome:84.0
e2e_service_worker_disable: false
e2e_block_popup: false
- e2e_docker_image_fragment: firefox:108.0
e2e_service_worker_disable: false
e2e_block_popup: false
- e2e_docker_image_fragment: firefox:78.0
e2e_service_worker_disable: false
e2e_block_popup: false
- e2e_docker_image_fragment: firefox:108.0
e2e_service_worker_disable: true
e2e_block_popup: false
- e2e_docker_image_fragment: firefox:78.0
e2e_service_worker_disable: true
e2e_block_popup: false
- e2e_docker_image_fragment: firefox:108.0
e2e_service_worker_disable: false
e2e_block_popup: true

runs-on: ubuntu-20.04
timeout-minutes: 10
Expand All @@ -47,4 +60,4 @@ jobs:
(cd e2e-test && npm ci) &
wait
- name: E2E test (${{ matrix.e2e_docker_image_fragment }})
run: cd e2e-test && E2E_DOCKER_IMAGE=selenium/standalone-${{ matrix.e2e_docker_image_fragment }} E2E_DISABLE_SERVICE_WORKER=${{ matrix.e2e_service_worker_disable }} npm start
run: cd e2e-test && E2E_DOCKER_IMAGE=selenium/standalone-${{ matrix.e2e_docker_image_fragment }} E2E_DISABLE_SERVICE_WORKER=${{ matrix.e2e_service_worker_disable }} E2E_BLOCK_POPUP=${{ matrix.e2e_block_popup }} npm start
10 changes: 9 additions & 1 deletion CHANGELOG.md
Expand Up @@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)

## [Unreleased]

## [0.14.0] - 2022-01-03
### Changed
- Improve retry-download
- Make UI height shorter to be the send button visible without scrolling for mobile users
- Update dependencies
- Remove <https://ppng.herokuapp.com> from public server URLs

## [0.13.0] - 2022-12-31
### Changed
- Passwordless protection by default
Expand Down Expand Up @@ -443,7 +450,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
### Added
- First release

[Unreleased]: https://github.com/nwtgck/piping-ui-web/compare/v0.13.0...HEAD
[Unreleased]: https://github.com/nwtgck/piping-ui-web/compare/v0.14.0...HEAD
[0.14.0]: https://github.com/nwtgck/piping-ui-web/compare/v0.13.0...v0.14.0
[0.13.0]: https://github.com/nwtgck/piping-ui-web/compare/v0.12.1...v0.13.0
[0.12.1]: https://github.com/nwtgck/piping-ui-web/compare/v0.12.0...v0.12.1
[0.12.0]: https://github.com/nwtgck/piping-ui-web/compare/v0.11.0...v0.12.0
Expand Down
60 changes: 48 additions & 12 deletions e2e-test/src/index.test.ts
Expand Up @@ -22,6 +22,7 @@ const PIPING_UI_URL = `http://localhost:${PIPING_UI_PORT}`;
const driverFactoryPromise = createDriverFactory({
dockerBaseImage: process.env["E2E_DOCKER_IMAGE"] || (() => {throw new Error("$E2E_DOCKER_IMAGE not found")})(),
disablesServiceWorker: process.env["E2E_DISABLE_SERVICE_WORKER"] === "true",
blockPopup: process.env["E2E_BLOCK_POPUP"] === "true",
forwardingTcpPorts: [PIPING_UI_PORT],
});

Expand Down Expand Up @@ -58,6 +59,33 @@ function getActions(driver: WebDriver) {
await (await elements.secretPathClearButton()).click();
await (await elements.secretPathInput()).sendKeys(path);
},
retryDownloadButtonIfNeed(): () => void {
let done = false;
(async () => {
let retryDownloadButton: webdriver.WebElement;
while (true) {
try {
retryDownloadButton = await driver.findElement(webdriver.By.css("[data-testid=retry_download_button]"));
const href = await retryDownloadButton.getAttribute("href");
if (href.includes("/sw-download/")) {
break;
}
} catch (e) {
}
await new Promise(resolve => setTimeout(resolve, 500));
if (done) {
return;
}
}
await driver.executeScript((button: HTMLElement) => {
window.scrollTo(0, button.offsetTop)
}, retryDownloadButton);
// GitHub Actions frequently fails in 2 seconds.
await new Promise(resolve => setTimeout(resolve, 5000));
await retryDownloadButton.click();
})().catch(e => console.error("failed to run retryDownloadButtonIfNeed()", e));
return () => { done = true };
},
};
}

Expand All @@ -73,8 +101,8 @@ describe('Piping UI', () => {
afterCallbacks.push(f);
}

it('should send and download a file', async () => {
const {sharePath, sharePathInDocker, downloadPath, createDriver} = await driverFactoryPromise;
it('should send and download a file', async function () {
const {sharePath, sharePathInDocker, downloadPath, createDriver, blockPopup} = await driverFactoryPromise;

const secretPath = crypto.randomBytes(8).toString("hex");
const transferContent = randomBytesAvoidingMimeTypeDetection(1024 * 1024);
Expand Down Expand Up @@ -110,6 +138,8 @@ describe('Piping UI', () => {
await nativeClick(driver, await elements.passwordlessSwitch());
await new Promise(resolve => setTimeout(resolve, 1000));
await (await elements.downloadButton()).click();
const finishRetryDownload = actions.retryDownloadButtonIfNeed();
defer(() => finishRetryDownload());

const downloadedFilePath = path.join(downloadPath, secretPath);
await waitForDownload(downloadedFilePath);
Expand All @@ -121,8 +151,9 @@ describe('Piping UI', () => {
}
});

it('should send and show a file', async () => {
const {sharePath, sharePathInDocker, downloadPath, createDriver} = await driverFactoryPromise;
it('should send and show a file', async function () {
const {sharePath, sharePathInDocker, downloadPath, createDriver, blockPopup} = await driverFactoryPromise;
// NOTE: Skip because GitHub Actions frequently failed. In local Intel Mac, it succeeds.

const secretPath = crypto.randomBytes(8).toString("hex");
{
Expand Down Expand Up @@ -165,8 +196,8 @@ describe('Piping UI', () => {
}
});

it('should send and download an E2E encrypted file with password', async () => {
const {sharePath, sharePathInDocker, downloadPath, createDriver} = await driverFactoryPromise;
it('should send and download an E2E encrypted file with password', async function () {
const {sharePath, sharePathInDocker, downloadPath, createDriver, blockPopup} = await driverFactoryPromise;

const secretPath = crypto.randomBytes(8).toString("hex");
const transferContent = randomBytesAvoidingMimeTypeDetection(1024 * 1024);
Expand Down Expand Up @@ -207,6 +238,8 @@ describe('Piping UI', () => {
await (await elements.passwordInput()).sendKeys(filePassword);
await new Promise(resolve => setTimeout(resolve, 1000));
await (await elements.downloadButton()).click();
const finishRetryDownload = actions.retryDownloadButtonIfNeed();
defer(() => finishRetryDownload());

const downloadedFilePath = path.join(downloadPath, secretPath);
await waitForDownload(downloadedFilePath);
Expand All @@ -218,8 +251,8 @@ describe('Piping UI', () => {
}
});

it('should send and show an E2E encrypted file with password', async () => {
const {sharePath, sharePathInDocker, downloadPath, createDriver} = await driverFactoryPromise;
it('should send and show an E2E encrypted file with password', async function () {
const {sharePath, sharePathInDocker, downloadPath, createDriver, blockPopup} = await driverFactoryPromise;

const secretPath = crypto.randomBytes(8).toString("hex");
const filePassword = crypto.randomBytes(32).toString("binary");
Expand Down Expand Up @@ -268,8 +301,8 @@ describe('Piping UI', () => {
}
});

it('should send and download a file by passwordless E2E encryption', async () => {
const {sharePath, sharePathInDocker, downloadPath, createDriver} = await driverFactoryPromise;
it('should send and download a file by passwordless E2E encryption', async function () {
const {sharePath, sharePathInDocker, downloadPath, createDriver, blockPopup} = await driverFactoryPromise;

const secretPath = crypto.randomBytes(8).toString("hex");
const transferContent = randomBytesAvoidingMimeTypeDetection(1024 * 1024);
Expand Down Expand Up @@ -304,6 +337,9 @@ describe('Piping UI', () => {
await new Promise(resolve => setTimeout(resolve, 2000));
await (await senderElements.passwordlessVerifiedButton0()).click();

const finishRetryDownload = receiverActions.retryDownloadButtonIfNeed();
defer(() => finishRetryDownload());

const downloadedFilePath = path.join(downloadPath, secretPath);
await waitForDownload(downloadedFilePath);
const downloadedFileContent = fs.readFileSync(downloadedFilePath);
Expand All @@ -313,8 +349,8 @@ describe('Piping UI', () => {
fs.rmSync(downloadedFilePath);
});

it('should send and show a file by passwordless E2E encryption', async () => {
const {sharePath, sharePathInDocker, downloadPath, createDriver} = await driverFactoryPromise;
it('should send and show a file by passwordless E2E encryption', async function () {
const {sharePath, sharePathInDocker, downloadPath, createDriver, blockPopup} = await driverFactoryPromise;

const secretPath = crypto.randomBytes(8).toString("hex");

Expand Down
23 changes: 19 additions & 4 deletions e2e-test/src/util.ts
Expand Up @@ -50,20 +50,29 @@ export async function servePipingUiIfNotServed(port: number) {
}
}

// NOTE: e.click() causes "Element <input id="..." type="checkbox"> is not clickable at point because another element <div class="..."> obscures it"
export async function nativeClick(driver: webdriver.WebDriver, element: webdriver.WebElement) {
await driver.executeScript((e: any) => e.click(), element);
}

export async function getBufferByBlobUrl(driver: webdriver.WebDriver, blobUrl: string): Promise<Buffer> {
const array: number[] = await driver.executeAsyncScript(async function (blobUrl: string) {
const base64String: string = await driver.executeAsyncScript(async function (blobUrl: string) {
// eslint-disable-next-line prefer-rest-params
const callback = arguments[arguments.length - 1];
const res = await window.fetch(blobUrl);
const arrayBuffer = await res.arrayBuffer();
// NOTE: transferring raw Uint8Array causes "Error: Accessing TypedArray data over Xrays is slow, and forbidden in order to encourage performant code. To copy TypedArrays across origin boundaries, consider using Components.utils.cloneInto()."
callback([...new Uint8Array(arrayBuffer)]);
// NOTE: Transferring Base64 string is faster than transferring number[] especially in Firefox
const array = [...new Uint8Array(arrayBuffer)];
// NOTE: chunking avoids an error "RangeError: too many function arguments"
const chunkSize = 65536;
let str = '';
while (array.length > 0) {
str += String.fromCharCode.apply(null, array.splice(0, chunkSize));
}
callback(btoa(str));
}, blobUrl);
return Buffer.from(array);
return Buffer.from(base64String, "base64");
}

export function randomBytesAvoidingMimeTypeDetection(size: number): Buffer {
Expand Down Expand Up @@ -94,7 +103,7 @@ export async function waitFor<T>(f: () => T | Promise<T>, { intervalMillis = 100

export const rayTracingPngImage: Buffer = fs.readFileSync("./resources/ray-tracing-iow-1280x720.png");

export async function createDriverFactory({dockerBaseImage, disablesServiceWorker, forwardingTcpPorts}: {dockerBaseImage: string, disablesServiceWorker: boolean, forwardingTcpPorts: readonly number[]}) {
export async function createDriverFactory({dockerBaseImage, disablesServiceWorker, blockPopup, forwardingTcpPorts}: {dockerBaseImage: string, disablesServiceWorker: boolean, blockPopup: boolean, forwardingTcpPorts: readonly number[]}) {
const sharePath = fs.mkdtempSync(path.join(os.tmpdir(), "selenium-docker-share-"));
const sharePathInDocker = "/home/seluser/tmp";
const downloadPath = fs.mkdtempSync(path.join(os.tmpdir(), "selenium-docker-downloads-share-"));
Expand Down Expand Up @@ -122,6 +131,8 @@ export async function createDriverFactory({dockerBaseImage, disablesServiceWorke
new firefox.Options()
.setPreference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream;application/x-unknown-content-type")
.setPreference("dom.serviceWorkers.enabled", !disablesServiceWorker)
// 20 is default value in Firefox 108
.setPreference("dom.popup_maximum", blockPopup ? 0 : 20)
)
.usingServer("http://localhost:4444/wd/hub").build();
}
Expand All @@ -130,6 +141,9 @@ export async function createDriverFactory({dockerBaseImage, disablesServiceWorke
if (disablesServiceWorker) {
throw new Error(`can not disable Service Worker on Chrome/Chromium`);
}
if (blockPopup) {
throw new Error(`can not block popup on Chrome/Chromium`);
}
return () => {
return new webdriver.Builder().forBrowser(webdriver.Browser.CHROME)
.usingServer("http://localhost:4444/wd/hub").build();
Expand All @@ -144,5 +158,6 @@ export async function createDriverFactory({dockerBaseImage, disablesServiceWorke
downloadPath,
downloadPathInDocker,
createDriver,
blockPopup,
};
}

0 comments on commit 5f4f5d2

Please sign in to comment.