diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 5a5f8e8d..6257a891 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -54,15 +54,12 @@ jobs: run: | choco install firefox-dev --pre echo "::add-path::C:\Program Files\Firefox Dev Edition" - - name: Pack Tridactyl - run: | - yarn make-zip - mv web-ext-artifacts/*.zip web-ext-artifacts/tridactyl.xpi - name: Test (Firefox) if: matrix.browser != 'chrome' env: HEADLESS: 1 run: | + yarn make-zip yarn jest || yarn jest || yarn jest || yarn jest # - name: Test (Chrome, Linux) # if: matrix.browser == 'chrome' && matrix.os == 'ubuntu' diff --git a/package.json b/package.json index 16a39c30..4d176461 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "make-zip": "web-ext build --source-dir build --overwrite-dest", "pretty": "bash scripts/pretty.sh", "run": "web-ext run -s build/ -u 'txti.es'", - "test": "yarn run build && rm -rf web-ext-artifacts/* && web-ext build --source-dir ./build --overwrite-dest && mv web-ext-artifacts/*.zip web-ext-artifacts/tridactyl.xpi && jest --silent", + "test": "yarn run build && web-ext build --source-dir ./build --overwrite-dest && jest --silent", "update-buildsystem": "rm -rf src/node_modules; yarn run clean", "watch": "echo 'watch is broken, use build instead'; exit 0;" }, diff --git a/tests/excmds.test.ts b/tests/excmds.test.ts index 61e2a6ce..a82e7550 100644 --- a/tests/excmds.test.ts +++ b/tests/excmds.test.ts @@ -10,86 +10,13 @@ import * as Until from "selenium-webdriver/lib/until" const {By} = webdriver import {Options} from "selenium-webdriver/firefox" +import { getNewestFileIn, sendKeys } from "./utils"; + jest.setTimeout(100000) // API docs because I waste too much time looking for them every time I go back to this: // https://seleniumhq.github.io/selenium/docs/api/javascript/ -const vimToSelenium = { - "Down": webdriver.Key.ARROW_DOWN, - "Left": webdriver.Key.ARROW_LEFT, - "Right": webdriver.Key.ARROW_RIGHT, - "Up": webdriver.Key.ARROW_UP, - "BS": webdriver.Key.BACK_SPACE, - "Del": webdriver.Key.DELETE, - "End": webdriver.Key.END, - "CR": webdriver.Key.ENTER, - "Esc": webdriver.Key.ESCAPE, - "Home": webdriver.Key.HOME, - "PageDown": webdriver.Key.PAGE_DOWN, - "PageUp": webdriver.Key.PAGE_UP, - "Tab": webdriver.Key.TAB, - "lt": "<", -} - -const modToSelenium = { - "A": webdriver.Key.ALT, - "C": webdriver.Key.CONTROL, - "M": webdriver.Key.META, - "S": webdriver.Key.SHIFT, -} - -function sendKeys (driver, keys) { - const delay = 10 - function chainRegularKeys (previousPromise, regularKeys) { - return regularKeys - .split("") - .reduce((p, key) => p - .then(() => driver.actions().sendKeys(key).perform()) - .then(() => driver.sleep(delay)) - , previousPromise) - } - function chainSpecialKey (previousPromise, specialKey) { - return previousPromise - .then(() => { - const noBrackets = specialKey.slice(1,-1) - if (noBrackets.includes("-")) { - const [modifiers, key] = noBrackets.split("-") - const mods = modifiers.split("").map(mod => modToSelenium[mod]) - return mods - .reduce((actions, mod) => actions.keyUp(mod), - mods.reduce((actions, mod) => actions.keyDown(mod), driver.actions()) - .sendKeys(vimToSelenium[key] || key)) - .perform() - } - return driver.actions().sendKeys(vimToSelenium[noBrackets] || noBrackets).perform() - }) - .then(() => driver.sleep(delay)) - } - - let result = Promise.resolve() - const regexp = /<[^>-]+-?[^>]*>/g - const specialKeys = keys.match(regexp) - if (!specialKeys) { - return chainRegularKeys(result, keys) - } - const regularKeys = keys.split(regexp) - let i - for (i = 0; i < Math.min(specialKeys.length, regularKeys.length); ++i) { - result = chainSpecialKey(chainRegularKeys(result, regularKeys[i]), specialKeys[i]) - } - if (i < regularKeys.length) { - result = regularKeys - .slice(i) - .reduce((previousPromise, currentKeys) => chainRegularKeys(previousPromise, currentKeys), result) - } - if ( i < specialKeys.length) { - result = specialKeys - .slice(i) - .reduce((previousPromise, currentKey) => chainSpecialKey(previousPromise, currentKey), result) - } - return result -} describe("webdriver", () => { @@ -98,9 +25,11 @@ describe("webdriver", () => { } async function getDriver() { - const dir = "web-ext-artifacts" - const extensionName = "tridactyl.xpi" - const extensionPath = dir + "/" + extensionName + const extensionPath = await getNewestFileIn(path.resolve("web-ext-artifacts")) + if (extensionPath === undefined) { + throw new Error("Couldn't find extension path"); + } + const options = (new Options()) .setPreference("xpinstall.signatures.required", false) .addExtensions(extensionPath) diff --git a/tests/utils.ts b/tests/utils.ts new file mode 100644 index 00000000..dc0715bb --- /dev/null +++ b/tests/utils.ts @@ -0,0 +1,106 @@ +import * as fs from "fs" +import * as path from "path" +import * as webdriver from "selenium-webdriver" + +// Returns the path of the newest file in directory +export async function getNewestFileIn(directory: string) { + // Get list of files + const names = ((await new Promise((resolve, reject) => { + fs.readdir(directory, (err: Error, filenames: string[]) => { + if (err) { + return reject(err) + } + return resolve(filenames) + }) + // Keep only files matching pattern + })) as string[]) + // Get their stat struct + const stats = await Promise.all(names.map(name => new Promise((resolve, reject) => { + const fpath = path.join(directory, name) + fs.stat(fpath, (err: any, stats) => { + if (err) { + reject(err) + } + (stats as any).path = fpath + return resolve(stats) + }) + }))) + // Sort by most recent and keep first + return ((stats.sort((stat1: any, stat2: any) => stat2.mtime - stat1.mtime)[0] || {}) as any).path +} + +const vimToSelenium = { + "Down": webdriver.Key.ARROW_DOWN, + "Left": webdriver.Key.ARROW_LEFT, + "Right": webdriver.Key.ARROW_RIGHT, + "Up": webdriver.Key.ARROW_UP, + "BS": webdriver.Key.BACK_SPACE, + "Del": webdriver.Key.DELETE, + "End": webdriver.Key.END, + "CR": webdriver.Key.ENTER, + "Esc": webdriver.Key.ESCAPE, + "Home": webdriver.Key.HOME, + "PageDown": webdriver.Key.PAGE_DOWN, + "PageUp": webdriver.Key.PAGE_UP, + "Tab": webdriver.Key.TAB, + "lt": "<", +} + +const modToSelenium = { + "A": webdriver.Key.ALT, + "C": webdriver.Key.CONTROL, + "M": webdriver.Key.META, + "S": webdriver.Key.SHIFT, +} + +export function sendKeys (driver, keys) { + const delay = 10 + function chainRegularKeys (previousPromise, regularKeys) { + return regularKeys + .split("") + .reduce((p, key) => p + .then(() => driver.actions().sendKeys(key).perform()) + .then(() => driver.sleep(delay)) + , previousPromise) + } + function chainSpecialKey (previousPromise, specialKey) { + return previousPromise + .then(() => { + const noBrackets = specialKey.slice(1,-1) + if (noBrackets.includes("-")) { + const [modifiers, key] = noBrackets.split("-") + const mods = modifiers.split("").map(mod => modToSelenium[mod]) + return mods + .reduce((actions, mod) => actions.keyUp(mod), + mods.reduce((actions, mod) => actions.keyDown(mod), driver.actions()) + .sendKeys(vimToSelenium[key] || key)) + .perform() + } + return driver.actions().sendKeys(vimToSelenium[noBrackets] || noBrackets).perform() + }) + .then(() => driver.sleep(delay)) + } + + let result = Promise.resolve() + const regexp = /<[^>-]+-?[^>]*>/g + const specialKeys = keys.match(regexp) + if (!specialKeys) { + return chainRegularKeys(result, keys) + } + const regularKeys = keys.split(regexp) + let i + for (i = 0; i < Math.min(specialKeys.length, regularKeys.length); ++i) { + result = chainSpecialKey(chainRegularKeys(result, regularKeys[i]), specialKeys[i]) + } + if (i < regularKeys.length) { + result = regularKeys + .slice(i) + .reduce((previousPromise, currentKeys) => chainRegularKeys(previousPromise, currentKeys), result) + } + if ( i < specialKeys.length) { + result = specialKeys + .slice(i) + .reduce((previousPromise, currentKey) => chainSpecialKey(previousPromise, currentKey), result) + } + return result +}