mirror of https://git.tuxpa.in/a/code-server.git
feat: add test for terminal echo to file
This commit is contained in:
parent
2bf0a0e76e
commit
cc99fddf24
|
@ -1,5 +1,5 @@
|
||||||
import { test, expect } from "@playwright/test"
|
import { test, expect } from "@playwright/test"
|
||||||
import { STORAGE } from "../utils/constants"
|
import { CODE_SERVER_ADDRESS, STORAGE } from "../utils/constants"
|
||||||
import { CodeServer } from "./models/CodeServer"
|
import { CodeServer } from "./models/CodeServer"
|
||||||
|
|
||||||
test.describe("CodeServer", () => {
|
test.describe("CodeServer", () => {
|
||||||
|
@ -23,24 +23,23 @@ test.describe("CodeServer", () => {
|
||||||
await codeServer.navigate()
|
await codeServer.navigate()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should open the default folder if not open", options, async ({ page }) => {
|
test("should navigate to the CODE_SERVER_ADDRESS", options, async ({ page }) => {
|
||||||
await codeServer.openFolder()
|
// We navigate codeServer before each test
|
||||||
|
// and we start the test with a storage state
|
||||||
|
// which means we should be logged in
|
||||||
|
// so it should be on the address
|
||||||
|
const url = page.url()
|
||||||
|
// We use match because there may be a / at the end
|
||||||
|
// so we don't want it to fail if we expect http://localhost:8080 to match http://localhost:8080/
|
||||||
|
expect(url).toMatch(CODE_SERVER_ADDRESS)
|
||||||
|
})
|
||||||
|
|
||||||
// find workspaceStorage in the Explorer menu, which would be open in the User folder
|
test("should always see the code-server editor", options, async ({ page }) => {
|
||||||
// which is the default folder that opens
|
expect(await codeServer.isEditorVisible()).toBe(true)
|
||||||
expect(await page.isVisible("text=workspaceStorage")).toBe(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should show the Integrated Terminal", options, async ({ page }) => {
|
test("should show the Integrated Terminal", options, async ({ page }) => {
|
||||||
await codeServer.viewTerminal()
|
await codeServer.focusTerminal()
|
||||||
expect(await page.isVisible("#terminal")).toBe(true)
|
expect(await page.isVisible("#terminal")).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should open a file with quickOpen", options, async ({ page }) => {
|
|
||||||
await codeServer.openFolder()
|
|
||||||
await codeServer.quickOpen("extensions.json")
|
|
||||||
// If the file is open, we will see an empty array
|
|
||||||
// assuming no extensions are installed
|
|
||||||
expect(await page.isVisible("text=[]"))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,53 +9,66 @@ export class CodeServer {
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to CODE_SERVER_ADDRESS
|
||||||
|
*/
|
||||||
async navigate() {
|
async navigate() {
|
||||||
await this.page.goto(CODE_SERVER_ADDRESS, { waitUntil: "networkidle" })
|
await this.page.goto(CODE_SERVER_ADDRESS, { waitUntil: "networkidle" })
|
||||||
|
|
||||||
|
let editorIsVisible = await this.isEditorVisible()
|
||||||
|
let reloadCount = 0
|
||||||
|
|
||||||
|
// Occassionally code-server timeouts in Firefox
|
||||||
|
// we're not sure why
|
||||||
|
// but usually a reload or two fixes it
|
||||||
|
// TODO@jsjoeio @oxy look into Firefox reconnection/timeout issues
|
||||||
|
// TODO@jsjoeio sometimes it's 2 reloads, othertimes it's 9
|
||||||
|
// double-check this logic
|
||||||
|
while (!editorIsVisible) {
|
||||||
|
reloadCount += 1
|
||||||
|
editorIsVisible = await this.isEditorVisible()
|
||||||
|
if (editorIsVisible) {
|
||||||
|
console.log(`Editor became visible after ${reloadCount} reloads`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
await this.page.reload({ waitUntil: "networkidle" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the editor is visible
|
||||||
|
*/
|
||||||
|
async isEditorVisible() {
|
||||||
// Make sure the editor actually loaded
|
// Make sure the editor actually loaded
|
||||||
await this.page.isVisible("div.monaco-workbench")
|
// If it's not visible after 2 seconds, something is wrong
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Opens the default folder /User if no arg passed
|
|
||||||
* @param absolutePath Example: /Users/jp/.local/share/code-server/User/
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
async openFolder(absolutePath?: string) {
|
|
||||||
// Check if no folder is opened
|
|
||||||
const folderIsNotOpen = await this.page.isVisible("text=You have not yet opened")
|
|
||||||
|
|
||||||
if (folderIsNotOpen) {
|
|
||||||
// Open the default folder
|
|
||||||
await this.page.keyboard.press("Meta+O")
|
|
||||||
await this.page.keyboard.press("Enter")
|
|
||||||
await this.page.waitForLoadState("networkidle")
|
await this.page.waitForLoadState("networkidle")
|
||||||
}
|
return await this.page.isVisible("div.monaco-workbench", { timeout: 5000 })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the integrated terminal if not already in view
|
* Focuses Integrated Terminal
|
||||||
* and focuses it
|
* by going to the Application Menu
|
||||||
|
* and clicking View > Terminal
|
||||||
*/
|
*/
|
||||||
async viewTerminal() {
|
|
||||||
// Check if Terminal is already in view
|
|
||||||
const isTerminalInView = await this.page.isVisible("#terminal")
|
|
||||||
|
|
||||||
if (!isTerminalInView) {
|
|
||||||
// Open using default keyboard shortcut
|
|
||||||
await this.focusTerminal()
|
|
||||||
await this.page.waitForSelector("#terminal")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async focusTerminal() {
|
async focusTerminal() {
|
||||||
await this.page.keyboard.press("Control+Backquote")
|
// If the terminal is already visible
|
||||||
|
// then we can focus it by hitting the keyboard shortcut
|
||||||
|
const isTerminalVisible = await this.page.isVisible("#terminal")
|
||||||
|
if (isTerminalVisible) {
|
||||||
|
await this.page.keyboard.press(`Meta+Backquote`)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
// Open using the manu
|
||||||
|
// Click [aria-label="Application Menu"] div[role="none"]
|
||||||
|
await this.page.click('[aria-label="Application Menu"] div[role="none"]')
|
||||||
|
|
||||||
async quickOpen(input: string) {
|
// Click text=View
|
||||||
await this.page.keyboard.press("Meta+P")
|
await this.page.hover("text=View")
|
||||||
await this.page.waitForSelector('[aria-describedby="quickInput_message"]')
|
await this.page.click("text=View")
|
||||||
await this.page.keyboard.type(input)
|
|
||||||
await this.page.waitForTimeout(2000)
|
// Click text=Terminal
|
||||||
await this.page.keyboard.press("Enter")
|
await this.page.hover("text=Terminal")
|
||||||
await this.page.waitForTimeout(2000)
|
await this.page.click("text=Terminal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { test, expect } from "@playwright/test"
|
import { test, expect } from "@playwright/test"
|
||||||
|
import * as fs from "fs"
|
||||||
|
import { tmpdir } from "os"
|
||||||
|
import * as path from "path"
|
||||||
|
|
||||||
import { STORAGE } from "../utils/constants"
|
import { STORAGE } from "../utils/constants"
|
||||||
import { CodeServer } from "./models/CodeServer"
|
import { CodeServer } from "./models/CodeServer"
|
||||||
|
|
||||||
|
@ -6,7 +10,7 @@ test.describe("Integrated Terminal", () => {
|
||||||
// Create a new context with the saved storage state
|
// Create a new context with the saved storage state
|
||||||
// so we don't have to logged in
|
// so we don't have to logged in
|
||||||
const options: any = {}
|
const options: any = {}
|
||||||
const testFileName = "hello.txt"
|
const testFileName = "test.txt"
|
||||||
const testString = "new string test from e2e test"
|
const testString = "new string test from e2e test"
|
||||||
let codeServer: CodeServer
|
let codeServer: CodeServer
|
||||||
|
|
||||||
|
@ -25,36 +29,34 @@ test.describe("Integrated Terminal", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should echo a string to a file", options, async ({ page }) => {
|
test("should echo a string to a file", options, async ({ page }) => {
|
||||||
// Open the default folder
|
const tmpFolderPath = fs.mkdtempSync(path.join(tmpdir(), "code-server-test"))
|
||||||
await codeServer.openFolder()
|
const tmpFile = `${tmpFolderPath}${path.sep}${testFileName}`
|
||||||
|
|
||||||
// Open terminal and type in value
|
// Open terminal and type in value
|
||||||
await codeServer.viewTerminal()
|
|
||||||
await codeServer.focusTerminal()
|
await codeServer.focusTerminal()
|
||||||
|
|
||||||
await page.keyboard.type(`echo '${testString}' >> ${testFileName}`)
|
// give the terminal a second to load
|
||||||
|
await page.waitForTimeout(3000)
|
||||||
|
await page.keyboard.type(`echo '${testString}' > ${tmpFile}`)
|
||||||
|
// Wait for the typing to finish before hitting enter
|
||||||
|
await page.waitForTimeout(500)
|
||||||
await page.keyboard.press("Enter")
|
await page.keyboard.press("Enter")
|
||||||
await page.waitForTimeout(2000)
|
await page.waitForTimeout(2000)
|
||||||
// It should show up on the left sidebar as a new file
|
|
||||||
const isFileVisible = await page.isVisible(`text="${testFileName}"`)
|
|
||||||
expect(isFileVisible).toBe(true)
|
|
||||||
|
|
||||||
if (isFileVisible) {
|
// .access checks if the file exists without opening it
|
||||||
// Check that the file has the test string in it
|
// it doesn't return anything hence why we expect it to
|
||||||
await codeServer.quickOpen(testFileName)
|
// resolve to undefined
|
||||||
expect(await page.isVisible(`text="${testString}"`)).toBe(true)
|
// If the promise rejects (i.e. the file doesn't exist)
|
||||||
|
// then the assertion will fail
|
||||||
|
await expect(fs.promises.access(tmpFile)).resolves.toBeUndefined()
|
||||||
|
|
||||||
// Clean up
|
await fs.promises.rmdir(tmpFolderPath, { recursive: true })
|
||||||
// Remove file
|
// Make sure neither file nor folder exist
|
||||||
await codeServer.focusTerminal()
|
// Note: We have to use ts-ignore because of an upstream typing error
|
||||||
await page.keyboard.type(`rm ${testFileName}`)
|
// See: https://github.com/microsoft/folio/issues/230#event-4621948411
|
||||||
await page.keyboard.press("Enter")
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
await page.waitForTimeout(2000)
|
// @ts-ignore
|
||||||
// Close the file from workbench
|
expect(fs.promises.access(tmpFile)).rejects.toThrowError(/no such file or directory/)
|
||||||
// otherwise it will still be visible
|
// @ts-ignore
|
||||||
// and our assertion will fail
|
expect(fs.promises.access(tmpFolderPath)).rejects.toThrowError(/no such file or directory/)
|
||||||
await page.keyboard.press(`Meta+W`)
|
|
||||||
expect(await page.isVisible(`text="${testString}"`)).toBe(false)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue