2019-10-18 13:05:37 +02:00
|
|
|
|
import "geckodriver"
|
2019-04-25 20:01:23 +02:00
|
|
|
|
|
2020-05-27 18:26:43 +02:00
|
|
|
|
import * as process from "process"
|
|
|
|
|
const env = process.env
|
2019-10-18 13:05:37 +02:00
|
|
|
|
import * as fs from "fs"
|
|
|
|
|
import * as os from "os"
|
|
|
|
|
import * as path from "path"
|
2019-10-18 14:39:47 +02:00
|
|
|
|
import * as webdriver from "selenium-webdriver"
|
|
|
|
|
import * as Until from "selenium-webdriver/lib/until"
|
|
|
|
|
const {By} = webdriver
|
2019-10-18 13:05:37 +02:00
|
|
|
|
import {Options} from "selenium-webdriver/firefox"
|
2019-04-25 20:01:23 +02:00
|
|
|
|
|
2020-05-29 06:46:58 +02:00
|
|
|
|
import { getNewestFileIn, sendKeys } from "./utils";
|
|
|
|
|
|
2020-05-27 22:37:16 +01:00
|
|
|
|
jest.setTimeout(100000)
|
2019-04-25 20:01:23 +02:00
|
|
|
|
|
2019-05-10 20:02:03 +02:00
|
|
|
|
// 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/
|
|
|
|
|
|
|
|
|
|
|
2019-04-25 20:01:23 +02:00
|
|
|
|
describe("webdriver", () => {
|
2019-05-18 15:10:37 +02:00
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
function iframeLoaded(driver) {
|
2019-10-18 14:39:47 +02:00
|
|
|
|
return driver.wait(Until.elementLocated(By.id("cmdline_iframe")))
|
2019-05-18 18:44:32 +02:00
|
|
|
|
}
|
2019-05-18 15:10:37 +02:00
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
async function getDriver() {
|
2020-05-29 06:46:58 +02:00
|
|
|
|
const extensionPath = await getNewestFileIn(path.resolve("web-ext-artifacts"))
|
|
|
|
|
if (extensionPath === undefined) {
|
|
|
|
|
throw new Error("Couldn't find extension path");
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 18:26:43 +02:00
|
|
|
|
const options = (new Options())
|
|
|
|
|
.setPreference("xpinstall.signatures.required", false)
|
|
|
|
|
.addExtensions(extensionPath)
|
|
|
|
|
if (env["HEADLESS"]) {
|
|
|
|
|
options.headless();
|
|
|
|
|
}
|
2019-05-18 18:44:32 +02:00
|
|
|
|
const driver = new webdriver.Builder()
|
|
|
|
|
.forBrowser("firefox")
|
2020-05-27 18:26:43 +02:00
|
|
|
|
.setFirefoxOptions(options)
|
2019-05-18 18:44:32 +02:00
|
|
|
|
.build()
|
|
|
|
|
// Wait until addon is loaded and :tutor is displayed
|
|
|
|
|
await iframeLoaded(driver)
|
|
|
|
|
// And wait a bit more otherwise Tridactyl won't be happy
|
|
|
|
|
await driver.sleep(500)
|
|
|
|
|
return driver
|
|
|
|
|
}
|
2019-04-25 20:01:23 +02:00
|
|
|
|
|
2019-05-19 18:18:49 +02:00
|
|
|
|
async function getDriverAndProfileDirs() {
|
2019-06-23 19:25:27 -03:00
|
|
|
|
const rootDir = os.tmpdir()
|
2019-05-19 18:18:49 +02:00
|
|
|
|
// First, find out what profile the driver is using
|
2019-06-23 19:25:27 -03:00
|
|
|
|
const profiles = fs.readdirSync(rootDir).map(p => path.join(rootDir, p))
|
2019-05-19 18:18:49 +02:00
|
|
|
|
const driver = await getDriver()
|
2019-06-23 19:25:27 -03:00
|
|
|
|
const newProfiles = fs.readdirSync(rootDir).map(p => path.join(rootDir, p))
|
2019-05-19 18:18:49 +02:00
|
|
|
|
.filter(p => p.match("moz") && !profiles.includes(p))
|
2019-06-23 19:25:27 -03:00
|
|
|
|
|
2020-06-21 12:20:42 +01:00
|
|
|
|
// Tridactyl's tmp profile detection is broken on windows and OSX
|
|
|
|
|
if (["win32", "darwin"].includes(os.platform())) {
|
2019-06-23 19:25:27 -03:00
|
|
|
|
await sendKeys(driver, `:set profiledir ${newProfiles[0]}<CR>`)
|
|
|
|
|
await driver.sleep(1000)
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-19 18:18:49 +02:00
|
|
|
|
return { driver, newProfiles }
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
async function killDriver(driver) {
|
|
|
|
|
try {
|
|
|
|
|
await driver.close()
|
|
|
|
|
} catch(e) {}
|
|
|
|
|
try {
|
|
|
|
|
await driver.quit()
|
|
|
|
|
} catch(e) {}
|
|
|
|
|
}
|
2019-04-25 20:01:23 +02:00
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
async function untilTabUrlMatches(driver, tabId, pattern) {
|
|
|
|
|
let match
|
|
|
|
|
do {
|
|
|
|
|
match = (await driver.executeScript(`return tri.browserBg.tabs.get(${tabId})`))
|
|
|
|
|
.url
|
|
|
|
|
.match(pattern)
|
|
|
|
|
} while (!match)
|
|
|
|
|
return match
|
|
|
|
|
}
|
2019-05-12 11:24:29 +02:00
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
async function newTabWithoutChangingOldTabs (driver, callback) {
|
|
|
|
|
const tabsBefore = await driver.executeScript("return tri.browserBg.tabs.query({})")
|
|
|
|
|
const result = await callback(tabsBefore);
|
|
|
|
|
const tabsAfter = await driver.wait(async () => {
|
|
|
|
|
let tabsAfter
|
|
|
|
|
do {
|
|
|
|
|
tabsAfter = await driver.executeScript("return tri.browserBg.tabs.query({})")
|
|
|
|
|
} while (tabsAfter.length == tabsBefore.length)
|
|
|
|
|
return tabsAfter
|
|
|
|
|
})
|
|
|
|
|
// A single new tab has been created
|
|
|
|
|
expect(tabsAfter.length).toBe(tabsBefore.length + 1)
|
2019-05-10 20:02:03 +02:00
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
// None of the previous tabs changed, except maybe for their index
|
|
|
|
|
const newtab = tabsAfter.find(tab2 => !tabsBefore.find(tab => tab.id == tab2.id))
|
|
|
|
|
const notNewTabs = tabsAfter.slice()
|
|
|
|
|
notNewTabs.splice(tabsAfter.findIndex(tab => tab == newtab), 1)
|
|
|
|
|
const ignoreValues = {
|
|
|
|
|
active: false, // the previously-active tab isn't necessarily active anymore
|
|
|
|
|
highlighted: true, // changing tabs changes highlights
|
|
|
|
|
index: 0, // indexes might not be the same depending on whether the new tab is
|
|
|
|
|
lastAccessed: 0, // lastAccessed has also changed for the previously-active tab
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < tabsBefore.length; ++i) {
|
|
|
|
|
let copy1 = Object.assign({}, tabsBefore[i], ignoreValues)
|
|
|
|
|
let copy2 = Object.assign({}, notNewTabs[i], ignoreValues)
|
|
|
|
|
expect(copy1).toEqual(copy2)
|
2019-05-11 15:26:34 +02:00
|
|
|
|
}
|
2019-05-18 18:44:32 +02:00
|
|
|
|
return [newtab, result]
|
|
|
|
|
}
|
2019-05-11 15:26:34 +02:00
|
|
|
|
|
2020-06-21 12:42:58 +01:00
|
|
|
|
// A simple ternany doesn't work inline :(
|
|
|
|
|
function testbutskipplatforms(...platforms){
|
2020-06-21 13:03:50 +01:00
|
|
|
|
if (platforms.includes(os.platform())) {
|
2020-06-21 12:42:58 +01:00
|
|
|
|
return test.skip
|
|
|
|
|
}
|
|
|
|
|
return test
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
test("`:rssexec` works", async () => {
|
|
|
|
|
const driver = await getDriver()
|
|
|
|
|
try {
|
|
|
|
|
await sendKeys(driver, ":set rsscmd js "
|
|
|
|
|
+ "const elem=document.createElement('span');"
|
|
|
|
|
+ "elem.id='rsscmdExecuted';"
|
|
|
|
|
+ "elem.innerText=`%u`;"
|
|
|
|
|
+ "document.body.appendChild(elem)<CR>")
|
2019-05-18 15:10:37 +02:00
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
// First, make sure completions are offered
|
2020-06-17 22:51:12 +01:00
|
|
|
|
await driver.get("file:///" + process.cwd() + "/e2e_tests/html/rss.html")
|
2019-05-18 18:44:32 +02:00
|
|
|
|
const iframe = await iframeLoaded(driver)
|
|
|
|
|
await sendKeys(driver, ":rssexec ")
|
|
|
|
|
await driver.switchTo().frame(iframe)
|
|
|
|
|
const elements = await driver.findElements(By.className("RssCompletionOption"))
|
|
|
|
|
expect(elements.length).toBeGreaterThan(3)
|
2019-05-19 19:34:39 +02:00
|
|
|
|
const url = await elements[0].getAttribute("innerText")
|
2019-05-18 15:10:37 +02:00
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
// Then, make sure rsscmd is executed and has the right arguments
|
|
|
|
|
await sendKeys(driver, "<Tab><CR>")
|
2019-10-18 13:05:37 +02:00
|
|
|
|
await (driver.switchTo() as any).parentFrame()
|
2019-10-18 14:39:47 +02:00
|
|
|
|
const elem = await driver.wait(Until.elementLocated(By.id("rsscmdExecuted")))
|
2019-05-19 19:34:39 +02:00
|
|
|
|
expect(url).toMatch(await elem.getAttribute("innerText"))
|
2019-05-18 18:44:32 +02:00
|
|
|
|
} catch (e) {
|
|
|
|
|
fail(e)
|
|
|
|
|
} finally {
|
|
|
|
|
await killDriver(driver)
|
|
|
|
|
}
|
|
|
|
|
})
|
2019-05-18 15:10:37 +02:00
|
|
|
|
|
2019-05-18 18:47:41 +02:00
|
|
|
|
test("`:editor` works", async () => {
|
|
|
|
|
const driver = await getDriver()
|
|
|
|
|
try {
|
|
|
|
|
const addedText = "There are %l lines and %c characters in this textarea."
|
2019-06-23 20:24:48 -03:00
|
|
|
|
|
|
|
|
|
if (os.platform() == "win32") {
|
|
|
|
|
await sendKeys(driver, `:set editorcmd echo | set /p text="${addedText}" >> %f<CR>`)
|
|
|
|
|
} else {
|
2020-06-20 14:01:37 +01:00
|
|
|
|
await sendKeys(driver, `:set editorcmd /bin/echo -n '${addedText}' >> %f<CR>`)
|
2019-06-23 20:24:48 -03:00
|
|
|
|
}
|
2019-05-18 18:47:41 +02:00
|
|
|
|
|
|
|
|
|
const areaId = "editorTest"
|
|
|
|
|
await driver.executeScript(`
|
|
|
|
|
const area = document.createElement("textarea")
|
|
|
|
|
area.id = "${areaId}"
|
|
|
|
|
document.body.appendChild(area)
|
|
|
|
|
area.focus()
|
|
|
|
|
`)
|
|
|
|
|
const text = "This is a line\nThis is another\nThis is a third."
|
|
|
|
|
await sendKeys(driver, text + "<C-i>")
|
|
|
|
|
await driver.sleep(1000)
|
|
|
|
|
expect(await driver.executeScript(`return document.getElementById("${areaId}").value`))
|
|
|
|
|
.toEqual(text + addedText.replace("%l", "3").replace("%c", "" + text.split("\n")[2].length))
|
|
|
|
|
} catch (e) {
|
|
|
|
|
fail(e)
|
|
|
|
|
} finally {
|
|
|
|
|
await killDriver(driver)
|
|
|
|
|
}
|
|
|
|
|
})
|
2019-05-12 11:24:29 +02:00
|
|
|
|
|
2020-06-21 12:42:58 +01:00
|
|
|
|
testbutskipplatforms("darwin")("`:guiset` works", async () => {
|
|
|
|
|
const { driver, newProfiles } = await getDriverAndProfileDirs()
|
|
|
|
|
try {
|
|
|
|
|
// Then, make sure `:guiset` is offering completions
|
|
|
|
|
const iframe = await iframeLoaded(driver)
|
|
|
|
|
await sendKeys(driver, ":guiset ")
|
|
|
|
|
await driver.switchTo().frame(iframe)
|
|
|
|
|
const elements = await driver.findElements(By.className("GuisetCompletionOption"))
|
|
|
|
|
expect(elements.length).toBeGreaterThan(0)
|
|
|
|
|
|
|
|
|
|
// Use whatever the first suggestion is
|
|
|
|
|
await sendKeys(driver, "<Tab> <Tab><CR>")
|
|
|
|
|
await driver.sleep(2000)
|
|
|
|
|
expect(await driver.executeScript(`return document.getElementById("tridactyl-input").value`))
|
2019-05-19 17:00:52 +02:00
|
|
|
|
.toEqual("userChrome.css written. Please restart Firefox to see the changes.")
|
2020-06-21 12:42:58 +01:00
|
|
|
|
expect(newProfiles.find(p => fs
|
|
|
|
|
.readdirSync(path.join(p, "chrome"))
|
|
|
|
|
.find(files => files.match("userChrome.css$")))
|
|
|
|
|
).toBeDefined()
|
|
|
|
|
} catch (e) {
|
|
|
|
|
fail(e)
|
|
|
|
|
} finally {
|
|
|
|
|
await killDriver(driver)
|
|
|
|
|
}
|
2019-05-19 17:00:52 +02:00
|
|
|
|
})
|
|
|
|
|
|
2019-05-19 17:43:41 +02:00
|
|
|
|
test("`:colourscheme` works", async () => {
|
|
|
|
|
const driver = await getDriver()
|
|
|
|
|
try {
|
|
|
|
|
expect(await driver.executeScript(`return document.documentElement.className`))
|
|
|
|
|
.toMatch("TridactylOwnNamespace TridactylThemeDefault")
|
|
|
|
|
await sendKeys(driver, ":colourscheme dark<CR>")
|
|
|
|
|
await driver.sleep(100)
|
|
|
|
|
expect(await driver.executeScript(`return document.documentElement.className`))
|
|
|
|
|
.toMatch("TridactylOwnNamespace TridactylThemeDark")
|
|
|
|
|
} catch (e) {
|
|
|
|
|
fail(e)
|
|
|
|
|
} finally {
|
2019-05-19 18:18:49 +02:00
|
|
|
|
await killDriver(driver)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test("`:setpref` works", async () => {
|
|
|
|
|
const { driver, newProfiles } = await getDriverAndProfileDirs()
|
|
|
|
|
try {
|
|
|
|
|
await sendKeys(driver, `:setpref a.b.c "d"<CR>`)
|
2019-06-23 19:25:27 -03:00
|
|
|
|
await driver.sleep(2000)
|
|
|
|
|
const file = fs.readFileSync(path.join(newProfiles[0], "user.js"), { encoding: "utf-8" })
|
2019-05-19 18:18:49 +02:00
|
|
|
|
expect(file).toMatch(/user_pref\("a.b.c", "d"\);/)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
fail(e)
|
|
|
|
|
} finally {
|
|
|
|
|
await killDriver(driver)
|
2019-05-19 17:43:41 +02:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2019-05-18 18:44:32 +02:00
|
|
|
|
test("`:tabopen<CR>` opens the newtab page.", async () => {
|
|
|
|
|
const driver = await getDriver()
|
|
|
|
|
return newTabWithoutChangingOldTabs(driver, async (tabsBefore) => {
|
|
|
|
|
await sendKeys(driver, ":tabopen<CR>")
|
|
|
|
|
}).then(async ([newtab, _]) => {
|
|
|
|
|
// The new tab is active
|
|
|
|
|
expect(newtab.active).toEqual(true)
|
|
|
|
|
// Its url is the newtab page's url
|
|
|
|
|
await driver.wait(untilTabUrlMatches(driver, newtab.id, new RegExp("moz-extension://.*/static/newtab.html")), 10000)
|
|
|
|
|
}).finally(() => killDriver(driver))
|
|
|
|
|
})
|
2019-05-12 11:24:29 +02:00
|
|
|
|
|
2020-08-02 13:13:42 +01:00
|
|
|
|
test("`:tabopen https://example.org<CR>` opens example.org.", async () => {
|
|
|
|
|
const driver = await getDriver()
|
|
|
|
|
return newTabWithoutChangingOldTabs(driver, async () => {
|
|
|
|
|
await sendKeys(driver, ":tabopen https://example.org<CR>")
|
|
|
|
|
}).then(async ([newtab, _]) => {
|
|
|
|
|
expect(newtab.active).toEqual(true)
|
|
|
|
|
await driver.wait(untilTabUrlMatches(driver, newtab.id, "https://example.org"), 10000)
|
|
|
|
|
}).finally(() => killDriver(driver))
|
|
|
|
|
})
|
2020-06-07 21:02:17 +01:00
|
|
|
|
|
2020-08-02 13:13:42 +01:00
|
|
|
|
test("`:tabopen qwant https://example.org<CR>` opens qwant.", async () => {
|
|
|
|
|
const driver = await getDriver()
|
|
|
|
|
return newTabWithoutChangingOldTabs(driver, async () => {
|
|
|
|
|
await sendKeys(driver, ":tabopen qwant https://example.org<CR>")
|
|
|
|
|
}).then(async ([newtab, _]) => {
|
|
|
|
|
expect(newtab.active).toEqual(true)
|
|
|
|
|
await driver.wait(untilTabUrlMatches(driver, newtab.id, new RegExp("^https://www.qwant.com/.*example.org")), 10000)
|
|
|
|
|
}).finally(() => killDriver(driver))
|
|
|
|
|
})
|
2020-06-07 21:02:17 +01:00
|
|
|
|
|
|
|
|
|
// test("`:tabopen test<CR>` opens google.", async () => {
|
|
|
|
|
// const driver = await getDriver()
|
|
|
|
|
// return newTabWithoutChangingOldTabs(driver, async () => {
|
|
|
|
|
// await sendKeys(driver, ":tabopen test<CR>")
|
|
|
|
|
// }).then(async ([newtab, _]) => {
|
|
|
|
|
// expect(newtab.active).toEqual(true)
|
|
|
|
|
// await driver.wait(untilTabUrlMatches(driver, newtab.id, new RegExp("^https://www.google.com/.*test")), 10000)
|
|
|
|
|
// }).finally(() => killDriver(driver))
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
// test("`:tabopen example.org<CR>` opens example.org.", async () => {
|
|
|
|
|
// const driver = await getDriver()
|
|
|
|
|
// return newTabWithoutChangingOldTabs(driver, async () => {
|
|
|
|
|
// await sendKeys(driver, ":tabopen example.org<CR>")
|
|
|
|
|
// }).then(async ([newtab, _]) => {
|
|
|
|
|
// expect(newtab.active).toEqual(true)
|
|
|
|
|
// await driver.wait(untilTabUrlMatches(driver, newtab.id, "example.org"), 10000)
|
|
|
|
|
// }).finally(() => killDriver(driver))
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
// test("`:tabopen search duckduckgo<CR>` opens google.", async () => {
|
|
|
|
|
// const driver = await getDriver()
|
|
|
|
|
// return newTabWithoutChangingOldTabs(driver, async () => {
|
|
|
|
|
// await sendKeys(driver, ":tabopen search duckduckgo<CR>")
|
|
|
|
|
// }).then(async ([newtab, _]) => {
|
|
|
|
|
// expect(newtab.active).toEqual(true)
|
|
|
|
|
// await driver.wait(untilTabUrlMatches(driver, newtab.id, new RegExp("^https://www.google.com/search.*duckduckgo")), 10000)
|
|
|
|
|
// }).finally(() => killDriver(driver))
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
// test("`:tabopen -b about:blank<CR>` opens a background tab.", async () => {
|
|
|
|
|
// const driver = await getDriver()
|
|
|
|
|
// return newTabWithoutChangingOldTabs(driver, async () => {
|
|
|
|
|
// await sendKeys(driver, ":tabopen -b about:blank<CR>")
|
|
|
|
|
// }).then(async ([newtab, _]) => {
|
|
|
|
|
// expect(newtab.active).toEqual(false)
|
|
|
|
|
// await driver.wait(untilTabUrlMatches(driver, newtab.id, "about:blank"))
|
|
|
|
|
// }).finally(() => killDriver(driver))
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
// test("`:tabopen -c work about:blank<CR>` opens about:blank in a container.", async () => {
|
|
|
|
|
// const driver = await getDriver()
|
|
|
|
|
// return newTabWithoutChangingOldTabs(driver, async () => {
|
|
|
|
|
// await sendKeys(driver, ":tabopen -c work about:blank<CR>")
|
|
|
|
|
// }).then(async ([newtab, _]) => {
|
|
|
|
|
// expect(newtab.active).toEqual(true)
|
|
|
|
|
// expect(newtab.cookieStoreId).toMatch("firefox-container-")
|
|
|
|
|
// await driver.wait(untilTabUrlMatches(driver, newtab.id, "about:blank"))
|
|
|
|
|
// }).finally(() => killDriver(driver))
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
// test("`:tabopen -b -c work search qwant<CR>` opens about:blank in a container.", async () => {
|
|
|
|
|
// const driver = await getDriver()
|
|
|
|
|
// return newTabWithoutChangingOldTabs(driver, async () => {
|
|
|
|
|
// await sendKeys(driver, ":tabopen -b -c work search qwant<CR>")
|
|
|
|
|
// }).then(async ([newtab, _]) => {
|
|
|
|
|
// expect(newtab.active).toEqual(false)
|
|
|
|
|
// expect(newtab.cookieStoreId).toMatch("firefox-container-")
|
|
|
|
|
// await driver.wait(untilTabUrlMatches(driver, newtab.id, new RegExp("^https://www.google.com/search.*qwant")))
|
|
|
|
|
// }).finally(() => killDriver(driver))
|
|
|
|
|
// })
|
2019-04-25 20:01:23 +02:00
|
|
|
|
})
|
2019-05-18 18:44:32 +02:00
|
|
|
|
|
|
|
|
|
// vim: tabstop=4 shiftwidth=4 expandtab
|