tridactyl/e2e_tests/e2e.test.ts
2022-11-10 00:58:06 +03:00

337 lines
14 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import "geckodriver"
import * as process from "process"
const env = process.env
import * as fs from "fs"
import * as os from "os"
import * as path from "path"
import * as webdriver from "selenium-webdriver"
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/
describe("webdriver", () => {
function iframeLoaded(driver) {
return driver.wait(Until.elementLocated(By.id("cmdline_iframe")))
}
async function getDriver() {
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)
if (env["HEADLESS"]) {
options.headless();
}
const driver = new webdriver.Builder()
.forBrowser("firefox")
.setFirefoxOptions(options)
.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
}
async function getDriverAndProfileDirs() {
const rootDir = os.tmpdir()
// First, find out what profile the driver is using
const profiles = fs.readdirSync(rootDir).map(p => path.join(rootDir, p))
const driver = await getDriver()
const newProfiles = fs.readdirSync(rootDir).map(p => path.join(rootDir, p))
.filter(p => p.match("moz") && !profiles.includes(p))
// Tridactyl's tmp profile detection is broken on windows and OSX
if (["win32", "darwin"].includes(os.platform())) {
await sendKeys(driver, `:set profiledir ${newProfiles[0]}<CR>`)
await driver.sleep(1000)
}
return { driver, newProfiles }
}
async function killDriver(driver) {
try {
await driver.close()
} catch(e) {}
try {
await driver.quit()
} catch(e) {}
}
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
}
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)
// 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)
}
return [newtab, result]
}
// A simple ternany doesn't work inline :(
function testbutskipplatforms(...platforms){
if (platforms.includes(os.platform())) {
return test.skip
}
return test
}
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>")
// First, make sure completions are offered
await driver.get("file:///" + process.cwd() + "/e2e_tests/html/rss.html")
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)
const url = await elements[0].getAttribute("innerText")
// Then, make sure rsscmd is executed and has the right arguments
await sendKeys(driver, "<Tab><CR>")
await (driver.switchTo() as any).parentFrame()
const elem = await driver.wait(Until.elementLocated(By.id("rsscmdExecuted")))
expect(url).toMatch(await elem.getAttribute("innerText"))
} catch (e) {
fail(e)
} finally {
await killDriver(driver)
}
})
test("`:editor` works", async () => {
const driver = await getDriver()
try {
const addedText = "There are %l lines and %c characters in this textarea."
if (os.platform() == "win32") {
await sendKeys(driver, `:set editorcmd echo | set /p text="${addedText}" >> %f<CR>`)
} else {
await sendKeys(driver, `:set editorcmd /bin/echo -n '${addedText}' >> %f<CR>`)
}
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)
}
})
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`))
.toEqual("userChrome.css written. Please restart Firefox to see the changes.")
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)
}
})
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 {
await killDriver(driver)
}
})
test("`:setpref` works", async () => {
const { driver, newProfiles } = await getDriverAndProfileDirs()
try {
await sendKeys(driver, `:setpref a.b.c "d"<CR>`)
await driver.sleep(2000)
const file = fs.readFileSync(path.join(newProfiles[0], "user.js"), { encoding: "utf-8" })
expect(file).toMatch(/user_pref\("a.b.c", "d"\);/)
} catch (e) {
fail(e)
} finally {
await killDriver(driver)
}
})
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))
})
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))
})
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))
})
// 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))
// })
})
// vim: tabstop=4 shiftwidth=4 expandtab