Archived
1
0

Refactor vscode endpoints to use fork directly.

This commit is contained in:
Teffen Ellis
2021-09-29 23:14:56 -04:00
committed by Teffen
parent beebf53adc
commit d8c344beda
52 changed files with 502 additions and 3406 deletions

View File

@ -4,7 +4,7 @@
"devDependencies": {
"@playwright/test": "^1.12.1",
"@types/jest": "^26.0.20",
"@types/jsdom": "^16.2.6",
"@types/jsdom": "^16.2.13",
"@types/node-fetch": "^2.5.8",
"@types/supertest": "^2.0.10",
"@types/wtfnode": "^0.7.0",

View File

@ -24,10 +24,8 @@ describe("login", () => {
const spy = jest.spyOn(document, "getElementById")
// Create a fake element and set the attribute
const mockElement = document.createElement("input")
mockElement.setAttribute("id", "base")
const expected = {
base: "./hello-world",
csStaticBase: "./static/development/Users/jp/Dev/code-server",
logLevel: 2,
disableTelemetry: false,
disableUpdateCheck: false,
@ -35,11 +33,6 @@ describe("login", () => {
mockElement.setAttribute("data-settings", JSON.stringify(expected))
document.body.appendChild(mockElement)
spy.mockImplementation(() => mockElement)
// Load file
require("../../../../src/browser/pages/login")
const el: HTMLInputElement | null = document.querySelector("input#base")
expect(el?.value).toBe("/hello-world")
})
})
describe("there is not an element with id 'base'", () => {
@ -76,15 +69,5 @@ describe("login", () => {
afterAll(() => {
jest.restoreAllMocks()
})
it("should do nothing", () => {
spy.mockImplementation(() => null)
// Load file
require("../../../../src/browser/pages/login")
// It's called once by getOptions in the top of the file
// and then another to get the base element
expect(spy).toHaveBeenCalledTimes(2)
})
})
})

View File

@ -1,400 +0,0 @@
/**
* @jest-environment jsdom
*/
import fetchMock from "jest-fetch-mock"
import { JSDOM } from "jsdom"
import {
getNlsConfiguration,
nlsConfigElementId,
getConfigurationForLoader,
setBodyBackgroundToThemeBackgroundColor,
_createScriptURL,
main,
createBundlePath,
} from "../../../../src/browser/pages/vscode"
describe("vscode", () => {
describe("getNlsConfiguration", () => {
let _document: Document
beforeEach(() => {
// We use underscores to not confuse with global values
const { window: _window } = new JSDOM()
_document = _window.document
fetchMock.enableMocks()
})
afterEach(() => {
fetchMock.resetMocks()
})
it("should throw an error if no nlsConfigElement", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not parse NLS configuration. Could not find nlsConfigElement with id: ${nlsConfigElementId}`
expect(() => {
getNlsConfiguration(_document, "")
}).toThrowError(errorMessage)
})
it("should throw an error if no nlsConfig", () => {
const mockElement = _document.createElement("div")
mockElement.setAttribute("id", nlsConfigElementId)
_document.body.appendChild(mockElement)
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not parse NLS configuration. Found nlsConfigElement but missing data-settings attribute.`
expect(() => {
getNlsConfiguration(_document, "")
}).toThrowError(errorMessage)
_document.body.removeChild(mockElement)
})
it("should return the correct configuration", () => {
const mockElement = _document.createElement("div")
const dataSettings = {
first: "Jane",
last: "Doe",
}
mockElement.setAttribute("id", nlsConfigElementId)
mockElement.setAttribute("data-settings", JSON.stringify(dataSettings))
_document.body.appendChild(mockElement)
const actual = getNlsConfiguration(_document, "")
expect(actual).toStrictEqual(dataSettings)
_document.body.removeChild(mockElement)
})
it("should return and have a loadBundle property if _resolvedLangaugePackCoreLocation", async () => {
const mockElement = _document.createElement("div")
const dataSettings = {
locale: "en",
availableLanguages: ["en", "de"],
_resolvedLanguagePackCoreLocation: "./",
}
mockElement.setAttribute("id", nlsConfigElementId)
mockElement.setAttribute("data-settings", JSON.stringify(dataSettings))
_document.body.appendChild(mockElement)
const nlsConfig = getNlsConfiguration(_document, "")
expect(nlsConfig._resolvedLanguagePackCoreLocation).not.toBe(undefined)
expect(nlsConfig.loadBundle).not.toBe(undefined)
const mockCallbackFn = jest.fn((_, bundle) => {
return bundle
})
fetchMock.mockOnce(JSON.stringify({ key: "hello world" }))
// Ensure that load bundle works as expected
// by mocking the fetch response and checking that the callback
// had the expected value
await nlsConfig.loadBundle("hello", "en", mockCallbackFn)
expect(mockCallbackFn).toHaveBeenCalledTimes(1)
expect(mockCallbackFn).toHaveBeenCalledWith(undefined, { key: "hello world" })
// Call it again to ensure it loads from the cache
// it should return the same value
await nlsConfig.loadBundle("hello", "en", mockCallbackFn)
expect(mockCallbackFn).toHaveBeenCalledTimes(2)
expect(mockCallbackFn).toHaveBeenCalledWith(undefined, { key: "hello world" })
fetchMock.mockReject(new Error("fake error message"))
const mockCallbackFn2 = jest.fn((error) => error)
// Call it for a different bundle and mock a failed fetch call
// to ensure we get the expected error
const error = await nlsConfig.loadBundle("goodbye", "es", mockCallbackFn2)
expect(error.message).toEqual("fake error message")
// Clean up
_document.body.removeChild(mockElement)
})
})
describe("createBundlePath", () => {
it("should return the correct path", () => {
const _resolvedLangaugePackCoreLocation = "./languages"
const bundle = "/bundle.js"
const expected = "./languages/!bundle.js.nls.json"
const actual = createBundlePath(_resolvedLangaugePackCoreLocation, bundle)
expect(actual).toBe(expected)
})
it("should return the correct path (even if _resolvedLangaugePackCoreLocation is undefined)", () => {
const _resolvedLangaugePackCoreLocation = undefined
const bundle = "/bundle.js"
const expected = "/!bundle.js.nls.json"
const actual = createBundlePath(_resolvedLangaugePackCoreLocation, bundle)
expect(actual).toBe(expected)
})
})
describe("setBodyBackgroundToThemeBackgroundColor", () => {
let _document: Document
let _localStorage: Storage
beforeEach(() => {
// We need to set the url in the JSDOM constructor
// to prevent this error "SecurityError: localStorage is not available for opaque origins"
// See: https://github.com/jsdom/jsdom/issues/2304#issuecomment-622314949
const { window: _window } = new JSDOM("", { url: "http://localhost" })
_document = _window.document
_localStorage = _window.localStorage
})
it("should return null", () => {
const test = {
colorMap: {
[`editor.background`]: "#ff3270",
},
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
expect(setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)).toBeNull()
_localStorage.removeItem("colorThemeData")
})
it("should throw an error if it can't find colorThemeData in localStorage", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. Could not find colorThemeData in localStorage.`
expect(() => {
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}).toThrowError(errorMessage)
})
it("should throw an error if there is an error parsing colorThemeData from localStorage", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. Could not parse colorThemeData from localStorage.`
_localStorage.setItem(
"colorThemeData",
'{"id":"vs-dark max-SS-Cyberpunk-themes-cyberpunk-umbra-color-theme-json","label":"Activate UMBRA protocol","settingsId":"Activate "errorForeground":"#ff3270","foreground":"#ffffff","sideBarTitle.foreground":"#bbbbbb"},"watch\\":::false}',
)
expect(() => {
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}).toThrowError(errorMessage)
localStorage.removeItem("colorThemeData")
})
it("should throw an error if there is no colorMap property", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. colorThemeData is missing colorMap.`
const test = {
id: "hey-joe",
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
expect(() => {
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}).toThrowError(errorMessage)
_localStorage.removeItem("colorThemeData")
})
it("should throw an error if there is no editor.background color", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. colorThemeData.colorMap["editor.background"] is undefined.`
const test = {
id: "hey-joe",
colorMap: {
editor: "#fff",
},
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
expect(() => {
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}).toThrowError(errorMessage)
_localStorage.removeItem("colorThemeData")
})
it("should set the body background to the editor background color", () => {
const test = {
colorMap: {
[`editor.background`]: "#ff3270",
},
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
// When the body.style.backgroundColor is set using hex
// it is converted to rgb
// which is why we use that in the assertion
expect(_document.body.style.backgroundColor).toBe("rgb(255, 50, 112)")
_localStorage.removeItem("colorThemeData")
})
})
describe("getConfigurationForLoader", () => {
let _window: Window
beforeEach(() => {
const { window: __window } = new JSDOM()
// @ts-expect-error the Window from JSDOM is not exactly the same as Window
// so we expect an error here
_window = __window
})
it("should return a loader object (with undefined trustedTypesPolicy)", () => {
const options = {
base: ".",
csStaticBase: "/",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const loader = getConfigurationForLoader({
options,
_window,
nlsConfig: nlsConfig,
})
expect(loader).toStrictEqual({
baseUrl: "http://localhost//vendor/modules/code-oss-dev/out",
paths: {
"iconv-lite-umd": "../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js",
jschardet: "../node_modules/jschardet/dist/jschardet.min.js",
"tas-client-umd": "../node_modules/tas-client-umd/lib/tas-client-umd.js",
"vscode-oniguruma": "../node_modules/vscode-oniguruma/release/main",
"vscode-textmate": "../node_modules/vscode-textmate/release/main",
xterm: "../node_modules/xterm/lib/xterm.js",
"xterm-addon-search": "../node_modules/xterm-addon-search/lib/xterm-addon-search.js",
"xterm-addon-unicode11": "../node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js",
"xterm-addon-webgl": "../node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js",
},
recordStats: true,
trustedTypesPolicy: undefined,
"vs/nls": {
availableLanguages: {},
first: "Jane",
last: "Doe",
locale: "en",
},
})
})
it("should return a loader object with trustedTypesPolicy", () => {
interface PolicyOptions {
createScriptUrl: (url: string) => string
}
function mockCreatePolicy(policyName: string, options: PolicyOptions) {
return {
name: policyName,
...options,
}
}
const mockFn = jest.fn(mockCreatePolicy)
// @ts-expect-error we are adding a custom property to window
_window.trustedTypes = {
createPolicy: mockFn,
}
const options = {
base: "/",
csStaticBase: "/",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const loader = getConfigurationForLoader({
options,
_window,
nlsConfig: nlsConfig,
})
expect(loader.trustedTypesPolicy).not.toBe(undefined)
expect(loader.trustedTypesPolicy.name).toBe("amdLoader")
// Check that we can actually create a script URL
// using the createScriptURL on the loader object
const scriptUrl = loader.trustedTypesPolicy.createScriptURL("http://localhost/foo.js")
expect(scriptUrl).toBe("http://localhost/foo.js")
})
})
describe("_createScriptURL", () => {
it("should return the correct url", () => {
const url = _createScriptURL("localhost/foo/bar.js", "localhost")
expect(url).toBe("localhost/foo/bar.js")
})
it("should throw if the value doesn't start with the origin", () => {
expect(() => {
_createScriptURL("localhost/foo/bar.js", "coder.com")
}).toThrow("Invalid script url: localhost/foo/bar.js")
})
})
describe("main", () => {
let _window: Window
let _document: Document
let _localStorage: Storage
beforeEach(() => {
// We need to set the url in the JSDOM constructor
// to prevent this error "SecurityError: localStorage is not available for opaque origins"
// See: https://github.com/jsdom/jsdom/issues/2304#issuecomment-62231494
const { window: __window } = new JSDOM("", { url: "http://localhost" })
// @ts-expect-error the Window from JSDOM is not exactly the same as Window
// so we expect an error here
_window = __window
_document = __window.document
_localStorage = __window.localStorage
const mockElement = _document.createElement("div")
const dataSettings = {
first: "Jane",
last: "Doe",
}
mockElement.setAttribute("id", nlsConfigElementId)
mockElement.setAttribute("data-settings", JSON.stringify(dataSettings))
_document.body.appendChild(mockElement)
const test = {
colorMap: {
[`editor.background`]: "#ff3270",
},
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
})
afterEach(() => {
_localStorage.removeItem("colorThemeData")
})
it("should throw if document is missing", () => {
expect(() => {
main(undefined, _window, _localStorage)
}).toThrow("document is undefined.")
})
it("should throw if window is missing", () => {
expect(() => {
main(_document, undefined, _localStorage)
}).toThrow("window is undefined.")
})
it("should throw if localStorage is missing", () => {
expect(() => {
main(_document, _window, undefined)
}).toThrow("localStorage is undefined.")
})
it("should add loader to self.require", () => {
main(_document, _window, _localStorage)
expect(Object.prototype.hasOwnProperty.call(self, "require")).toBe(true)
})
it("should not throw in browser context", () => {
// Assuming we call it in a normal browser context
// where everything is defined
expect(() => {
main(_document, _window, _localStorage)
}).not.toThrow()
})
})
})

View File

@ -1,183 +0,0 @@
import { JSDOM } from "jsdom"
import { registerServiceWorker } from "../../../src/browser/register"
import { createLoggerMock } from "../../utils/helpers"
import { LocationLike } from "../common/util.test"
describe("register", () => {
describe("when navigator and serviceWorker are defined", () => {
const mockRegisterFn = jest.fn()
beforeAll(() => {
const { window } = new JSDOM()
global.window = window as unknown as Window & typeof globalThis
global.document = window.document
global.navigator = window.navigator
global.location = window.location
Object.defineProperty(global.navigator, "serviceWorker", {
value: {
register: mockRegisterFn,
},
})
})
const loggerModule = createLoggerMock()
beforeEach(() => {
jest.clearAllMocks()
jest.mock("@coder/logger", () => loggerModule)
})
afterEach(() => {
jest.resetModules()
})
afterAll(() => {
jest.restoreAllMocks()
// We don't want these to stay around because it can affect other tests
global.window = undefined as unknown as Window & typeof globalThis
global.document = undefined as unknown as Document & typeof globalThis
global.navigator = undefined as unknown as Navigator & typeof globalThis
global.location = undefined as unknown as Location & typeof globalThis
})
it("test should have access to browser globals from beforeAll", () => {
expect(typeof global.window).not.toBeFalsy()
expect(typeof global.document).not.toBeFalsy()
expect(typeof global.navigator).not.toBeFalsy()
expect(typeof global.location).not.toBeFalsy()
})
it("should register a ServiceWorker", () => {
// Load service worker like you would in the browser
require("../../../src/browser/register")
expect(mockRegisterFn).toHaveBeenCalled()
expect(mockRegisterFn).toHaveBeenCalledTimes(1)
})
it("should log an error if something doesn't work", () => {
const message = "Can't find browser"
const error = new Error(message)
mockRegisterFn.mockImplementation(() => {
throw error
})
// Load service worker like you would in the browser
require("../../../src/browser/register")
expect(mockRegisterFn).toHaveBeenCalled()
expect(loggerModule.logger.error).toHaveBeenCalled()
expect(loggerModule.logger.error).toHaveBeenCalledTimes(1)
expect(loggerModule.logger.error).toHaveBeenCalledWith(
`[Service Worker] registration: ${error.message} ${error.stack}`,
)
})
})
describe("when navigator and serviceWorker are NOT defined", () => {
const loggerModule = createLoggerMock()
beforeEach(() => {
jest.clearAllMocks()
jest.mock("@coder/logger", () => loggerModule)
})
afterAll(() => {
jest.restoreAllMocks()
})
it("should log an error", () => {
// Load service worker like you would in the browser
require("../../../src/browser/register")
expect(loggerModule.logger.error).toHaveBeenCalled()
expect(loggerModule.logger.error).toHaveBeenCalledTimes(1)
expect(loggerModule.logger.error).toHaveBeenCalledWith("[Service Worker] navigator is undefined")
})
})
describe("registerServiceWorker", () => {
let serviceWorkerPath: string
let serviceWorkerScope: string
const mockFn = jest.fn((path: string, options: { scope: string }) => {
serviceWorkerPath = path
serviceWorkerScope = options.scope
return undefined
})
beforeAll(() => {
const location: LocationLike = {
pathname: "",
origin: "http://localhost:8080",
}
const { window } = new JSDOM()
global.window = window as unknown as Window & typeof globalThis
global.document = window.document
global.navigator = window.navigator
global.location = location as Location
Object.defineProperty(global.navigator, "serviceWorker", {
value: {
register: mockFn,
},
})
})
afterEach(() => {
mockFn.mockClear()
jest.resetModules()
})
afterAll(() => {
jest.restoreAllMocks()
// We don't want these to stay around because it can affect other tests
global.window = undefined as unknown as Window & typeof globalThis
global.document = undefined as unknown as Document & typeof globalThis
global.navigator = undefined as unknown as Navigator & typeof globalThis
global.location = undefined as unknown as Location & typeof globalThis
})
it("should register when options.base is undefined", async () => {
// Mock getElementById
const csStaticBasePath = "/static/development/Users/jp/Dev/code-server"
const spy = jest.spyOn(document, "getElementById")
// Create a fake element and set the attribute
const mockElement = document.createElement("div")
mockElement.id = "coder-options"
mockElement.setAttribute(
"data-settings",
`{"csStaticBase":"${csStaticBasePath}","logLevel":2,"disableUpdateCheck":false}`,
)
// Return mockElement from the spy
// this way, when we call "getElementById"
// it returns the element
spy.mockImplementation(() => mockElement)
await registerServiceWorker()
expect(mockFn).toBeCalled()
expect(serviceWorkerPath).toMatch(`${csStaticBasePath}/out/browser/serviceWorker.js`)
expect(serviceWorkerScope).toMatch("/")
})
it("should register when options.base is defined", async () => {
const csStaticBasePath = "/static/development/Users/jp/Dev/code-server"
const spy = jest.spyOn(document, "getElementById")
// Create a fake element and set the attribute
const mockElement = document.createElement("div")
mockElement.id = "coder-options"
mockElement.setAttribute(
"data-settings",
`{"base":"proxy/","csStaticBase":"${csStaticBasePath}","logLevel":2,"disableUpdateCheck":false}`,
)
// Return mockElement from the spy
// this way, when we call "getElementById"
// it returns the element
spy.mockImplementation(() => mockElement)
await registerServiceWorker()
expect(mockFn).toBeCalled()
expect(serviceWorkerPath).toMatch(`/out/browser/serviceWorker.js`)
expect(serviceWorkerScope).toMatch("/")
})
})
})

View File

@ -1,92 +0,0 @@
interface MockEvent {
claim: jest.Mock<any, any>
waitUntil?: jest.Mock<any, any>
}
interface Listener {
event: string
cb: (event?: MockEvent) => void
}
describe("serviceWorker", () => {
let listeners: Listener[] = []
let spy: jest.SpyInstance
let claimSpy: jest.Mock<any, any>
let waitUntilSpy: jest.Mock<any, any>
function emit(event: string) {
listeners
.filter((listener) => listener.event === event)
.forEach((listener) => {
switch (event) {
case "activate":
listener.cb({
claim: jest.fn(),
waitUntil: jest.fn(() => waitUntilSpy()),
})
break
default:
listener.cb()
}
})
}
beforeEach(() => {
claimSpy = jest.fn()
spy = jest.spyOn(console, "log")
waitUntilSpy = jest.fn()
Object.assign(global, {
self: global,
addEventListener: (event: string, cb: () => void) => {
listeners.push({ event, cb })
},
clients: {
claim: claimSpy.mockResolvedValue("claimed"),
},
})
})
afterEach(() => {
jest.restoreAllMocks()
jest.resetModules()
spy.mockClear()
claimSpy.mockClear()
// Clear all the listeners
listeners = []
})
it("should add 3 listeners: install, activate and fetch", () => {
require("../../../src/browser/serviceWorker.ts")
const listenerEventNames = listeners.map((listener) => listener.event)
expect(listeners).toHaveLength(3)
expect(listenerEventNames).toContain("install")
expect(listenerEventNames).toContain("activate")
expect(listenerEventNames).toContain("fetch")
})
it("should call the proper callbacks for 'install'", async () => {
require("../../../src/browser/serviceWorker.ts")
emit("install")
expect(spy).toHaveBeenCalledWith("[Service Worker] installed")
expect(spy).toHaveBeenCalledTimes(1)
})
it("should do nothing when 'fetch' is called", async () => {
require("../../../src/browser/serviceWorker.ts")
emit("fetch")
expect(spy).not.toHaveBeenCalled()
})
it("should call the proper callbacks for 'activate'", async () => {
require("../../../src/browser/serviceWorker.ts")
emit("activate")
// Activate serviceWorker
expect(spy).toHaveBeenCalledWith("[Service Worker] activated")
expect(waitUntilSpy).toHaveBeenCalled()
expect(claimSpy).toHaveBeenCalled()
})
})

View File

@ -131,7 +131,7 @@ describe("util", () => {
})
it("should return options with base and cssStaticBase even if it doesn't exist", () => {
expect(util.getOptions()).toStrictEqual({
expect(util.getClientConfiguration()).toStrictEqual({
base: "",
csStaticBase: "",
})
@ -151,7 +151,7 @@ describe("util", () => {
// it returns the element
spy.mockImplementation(() => mockElement)
expect(util.getOptions()).toStrictEqual({
expect(util.getClientConfiguration()).toStrictEqual({
base: "",
csStaticBase: "/static/development/Users/jp/Dev/code-server",
disableUpdateCheck: false,
@ -167,7 +167,7 @@ describe("util", () => {
// spreads the original options
// then parses the queryOpts
location.search = '?options={"logLevel":2}'
expect(util.getOptions()).toStrictEqual({
expect(util.getClientConfiguration()).toStrictEqual({
base: "",
csStaticBase: "",
logLevel: 2,
@ -194,20 +194,6 @@ describe("util", () => {
})
})
describe("getFirstString", () => {
it("should return the string if passed a string", () => {
expect(util.getFirstString("Hello world!")).toBe("Hello world!")
})
it("should get the first string from an array", () => {
expect(util.getFirstString(["Hello", "World"])).toBe("Hello")
})
it("should return undefined if the value isn't an array or a string", () => {
expect(util.getFirstString({ name: "Coder" })).toBe(undefined)
})
})
describe("logError", () => {
afterEach(() => {
jest.clearAllMocks()

View File

@ -122,26 +122,6 @@ describe("createApp", () => {
expect(unlinkSpy).toHaveBeenCalledTimes(1)
server.close()
})
it("should catch errors thrown when unlinking a socket", async () => {
const tmpDir2 = await tmpdir("unlink-socket-error")
const tmpFile = path.join(tmpDir2, "unlink-socket-file")
// await promises.writeFile(tmpFile, "")
const socketPath = tmpFile
const defaultArgs = await setDefaults({
_: [],
socket: socketPath,
})
const app = await createApp(defaultArgs)
const server = app[2]
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(`ENOENT: no such file or directory, unlink '${socketPath}'`)
server.close()
// Ensure directory was removed
rmdirSync(tmpDir2, { recursive: true })
})
it("should create an https server if args.cert exists", async () => {
const testCertificate = await generateCertificate("localhost")

View File

@ -1,10 +1,16 @@
import { promises as fs } from "fs"
import * as path from "path"
import { rootPath } from "../../../../src/node/constants"
import { tmpdir } from "../../../utils/helpers"
import * as httpserver from "../../../utils/httpserver"
import * as integration from "../../../utils/integration"
describe("/static", () => {
const NOT_FOUND = {
code: 404,
message: "not found",
}
describe("/_static", () => {
let _codeServer: httpserver.HttpServer | undefined
function codeServer(): httpserver.HttpServer {
if (!_codeServer) {
@ -17,14 +23,8 @@ describe("/static", () => {
let testFileContent: string | undefined
let nonExistentTestFile: string | undefined
// The static endpoint expects a commit and then the full path of the file.
// The commit is just for cache busting so we can use anything we want. `-`
// and `development` are specially recognized in that they will cause the
// static endpoint to avoid sending cache headers.
const commit = "-"
beforeAll(async () => {
const testDir = await tmpdir("static")
const testDir = await tmpdir("_static")
testFile = path.join(testDir, "test")
testFileContent = "static file contents"
nonExistentTestFile = path.join(testDir, "i-am-not-here")
@ -39,20 +39,12 @@ describe("/static", () => {
})
function commonTests() {
it("should return a 404 when a commit and file are not provided", async () => {
const resp = await codeServer().fetch("/static")
expect(resp.status).toBe(404)
const content = await resp.json()
expect(content).toStrictEqual({ error: "Not Found" })
})
it("should return a 404 when a file is not provided", async () => {
const resp = await codeServer().fetch(`/static/${commit}`)
expect(resp.status).toBe(404)
const resp = await codeServer().fetch(`/_static/`)
expect(resp.status).toBe(NOT_FOUND.code)
const content = await resp.json()
expect(content).toStrictEqual({ error: "Not Found" })
expect(content.error).toContain(NOT_FOUND.message)
})
}
@ -64,73 +56,22 @@ describe("/static", () => {
commonTests()
it("should return a 404 for a nonexistent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}/${nonExistentTestFile}`)
expect(resp.status).toBe(404)
const filePath = path.join("/_static/", nonExistentTestFile!)
const content = await resp.json()
expect(content.error).toMatch("ENOENT")
const resp = await codeServer().fetch(filePath)
expect(resp.status).toBe(NOT_FOUND.code)
})
it("should return a 200 and file contents for an existent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}${testFile}`)
const resp = await codeServer().fetch("/_static/src/browser/robots.txt")
expect(resp.status).toBe(200)
const localFilePath = path.join(rootPath, "src/browser/robots.txt")
const localFileContent = await fs.readFile(localFilePath, "utf8")
// console.log(localFileContent)
const content = await resp.text()
expect(content).toStrictEqual(testFileContent)
})
})
describe("enabled authentication", () => {
// Store whatever might be in here so we can restore it afterward.
// TODO: We should probably pass this as an argument somehow instead of
// manipulating the environment.
const previousEnvPassword = process.env.PASSWORD
beforeEach(async () => {
process.env.PASSWORD = "test"
_codeServer = await integration.setup(["--auth=password"], "")
})
afterEach(() => {
process.env.PASSWORD = previousEnvPassword
})
commonTests()
describe("inside code-server root", () => {
it("should return a 404 for a nonexistent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}/${__filename}-does-not-exist`)
expect(resp.status).toBe(404)
const content = await resp.json()
expect(content.error).toMatch("ENOENT")
})
it("should return a 200 and file contents for an existent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}${__filename}`)
expect(resp.status).toBe(200)
const content = await resp.text()
expect(content).toStrictEqual(await fs.readFile(__filename, "utf8"))
})
})
describe("outside code-server root", () => {
it("should return a 401 for a nonexistent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}/${nonExistentTestFile}`)
expect(resp.status).toBe(401)
const content = await resp.json()
expect(content).toStrictEqual({ error: "Unauthorized" })
})
it("should return a 401 for an existent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}${testFile}`)
expect(resp.status).toBe(401)
const content = await resp.json()
expect(content).toStrictEqual({ error: "Unauthorized" })
})
expect(content).toStrictEqual(localFileContent)
})
})
})

View File

@ -457,31 +457,6 @@ describe("escapeHtml", () => {
})
})
describe("pathToFsPath", () => {
it("should convert a path to a file system path", () => {
expect(util.pathToFsPath("/foo/bar/baz")).toBe("/foo/bar/baz")
})
it("should lowercase drive letter casing by default", () => {
expect(util.pathToFsPath("/C:/far/boo")).toBe("c:/far/boo")
})
it("should keep drive letter casing when set to true", () => {
expect(util.pathToFsPath("/C:/far/bo", true)).toBe("C:/far/bo")
})
it("should replace / with \\ on Windows", () => {
const ORIGINAL_PLATFORM = process.platform
Object.defineProperty(process, "platform", {
value: "win32",
})
expect(util.pathToFsPath("/C:/far/boo")).toBe("c:\\far\\boo")
Object.defineProperty(process, "platform", {
value: ORIGINAL_PLATFORM,
})
})
})
describe("isFile", () => {
const testDir = path.join(tmpdir, "tests", "isFile")
let pathToFile = ""

View File

@ -1101,10 +1101,10 @@
jest-diff "^26.0.0"
pretty-format "^26.0.0"
"@types/jsdom@^16.2.6":
version "16.2.6"
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.6.tgz#9ddf0521e49be5365797e690c3ba63148e562c29"
integrity sha512-yQA+HxknGtW9AkRTNyiSH3OKW5V+WzO8OPTdne99XwJkYC+KYxfNIcoJjeiSqP3V00PUUpFP6Myoo9wdIu78DQ==
"@types/jsdom@^16.2.13":
version "16.2.13"
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.13.tgz#126c8b7441b159d6234610a48de77b6066f1823f"
integrity sha512-8JQCjdeAidptSsOcRWk2iTm9wCcwn9l+kRG6k5bzUacrnm1ezV4forq0kWjUih/tumAeoG+OspOvQEbbRucBTw==
dependencies:
"@types/node" "*"
"@types/parse5" "*"