Merge branch 'master' into upgrade-vscode-1.53
This commit is contained in:
commit
9ea18636d7
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@ -1 +1,3 @@
|
|||||||
|
* @cdr/code-server-reviewers
|
||||||
|
|
||||||
ci/helm-chart @Matthew-Beckett @alexgorbatchev
|
ci/helm-chart @Matthew-Beckett @alexgorbatchev
|
||||||
|
10
.github/workflows/ci.yaml
vendored
10
.github/workflows/ci.yaml
vendored
@ -24,6 +24,9 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
needs: linux-amd64
|
needs: linux-amd64
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
PASSWORD: e45432jklfdsab
|
||||||
|
CODE_SERVER_ADDRESS: http://localhost:8080
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Download release packages
|
- name: Download release packages
|
||||||
@ -37,9 +40,14 @@ jobs:
|
|||||||
- uses: microsoft/playwright-github-action@v1
|
- uses: microsoft/playwright-github-action@v1
|
||||||
- name: Install dependencies and run tests
|
- name: Install dependencies and run tests
|
||||||
run: |
|
run: |
|
||||||
node ./release-packages/code-server*-linux-amd64 &
|
./release-packages/code-server*-linux-amd64/bin/code-server --home $CODE_SERVER_ADDRESS/healthz &
|
||||||
yarn --frozen-lockfile
|
yarn --frozen-lockfile
|
||||||
yarn test
|
yarn test
|
||||||
|
- name: Upload test artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: test-videos
|
||||||
|
path: ./test/videos
|
||||||
|
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -15,4 +15,6 @@ node-*
|
|||||||
/lib/coder-cloud-agent
|
/lib/coder-cloud-agent
|
||||||
.home
|
.home
|
||||||
coverage
|
coverage
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
test/videos
|
||||||
|
test/screenshots
|
||||||
|
@ -43,8 +43,9 @@ Make sure you have `$GITHUB_TOKEN` set and [hub](https://github.com/github/hub)
|
|||||||
9. Update the AUR package.
|
9. Update the AUR package.
|
||||||
- Instructions on updating the AUR package are at [cdr/code-server-aur](https://github.com/cdr/code-server-aur).
|
- Instructions on updating the AUR package are at [cdr/code-server-aur](https://github.com/cdr/code-server-aur).
|
||||||
10. Wait for the npm package to be published.
|
10. Wait for the npm package to be published.
|
||||||
11. Update the homebrew package.
|
11. Update the [homebrew package](https://github.com/Homebrew/homebrew-core/blob/master/Formula/code-server.rb).
|
||||||
- Send a pull request to [homebrew-core](https://github.com/Homebrew/homebrew-core) with the URL in the [formula](https://github.com/Homebrew/homebrew-core/blob/master/Formula/code-server.rb) updated.
|
1. Install [homebrew](https://brew.sh/)
|
||||||
|
2. Run `brew bump-formula-pr --version=3.8.1 code-server` and update the version accordingly. This will bump the version and open a PR. Note: this will only work once the version is published on npm.
|
||||||
|
|
||||||
## Updating Code Coverage in README
|
## Updating Code Coverage in README
|
||||||
|
|
||||||
|
@ -9,6 +9,18 @@ main() {
|
|||||||
# information. We must also run it from the root otherwise coverage will not
|
# information. We must also run it from the root otherwise coverage will not
|
||||||
# include our source files.
|
# include our source files.
|
||||||
cd "$OLDPWD"
|
cd "$OLDPWD"
|
||||||
|
if [[ -z ${PASSWORD-} ]] || [[ -z ${CODE_SERVER_ADDRESS-} ]]; then
|
||||||
|
echo "The end-to-end testing suites rely on your local environment"
|
||||||
|
echo -e "\n"
|
||||||
|
echo "Please set the following environment variables locally:"
|
||||||
|
echo " \$PASSWORD"
|
||||||
|
echo " \$CODE_SERVER_ADDRESS"
|
||||||
|
echo -e "\n"
|
||||||
|
echo "Please make sure you have code-server running locally with the flag:"
|
||||||
|
echo " --home \$CODE_SERVER_ADDRESS/healthz "
|
||||||
|
echo -e "\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
CS_DISABLE_PLUGINS=true ./test/node_modules/.bin/jest "$@"
|
CS_DISABLE_PLUGINS=true ./test/node_modules/.bin/jest "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +35,9 @@ There are several differences, however. You must:
|
|||||||
- Use Node.js version 12.x (or greater)
|
- Use Node.js version 12.x (or greater)
|
||||||
- Have [yarn](https://classic.yarnpkg.com/en/) installed (which is used to install JS packages and run development scripts)
|
- Have [yarn](https://classic.yarnpkg.com/en/) installed (which is used to install JS packages and run development scripts)
|
||||||
- Have [nfpm](https://github.com/goreleaser/nfpm) (which is used to build `.deb` and `.rpm` packages and [jq](https://stedolan.github.io/jq/) (used to build code-server releases) installed
|
- Have [nfpm](https://github.com/goreleaser/nfpm) (which is used to build `.deb` and `.rpm` packages and [jq](https://stedolan.github.io/jq/) (used to build code-server releases) installed
|
||||||
|
- Have [shfmt](https://pkg.go.dev/mvdan.cc/sh/v3) installed to run `yarn fmt` (requires Go is installed on your system)
|
||||||
|
|
||||||
The [CI container](../ci/images/debian8/Dockerfile) is a useful reference for all
|
The [CI container](../ci/images/debian10/Dockerfile) is a useful reference for all
|
||||||
of the dependencies code-server uses.
|
of the dependencies code-server uses.
|
||||||
|
|
||||||
## Development Workflow
|
## Development Workflow
|
||||||
@ -120,10 +121,10 @@ node ./release
|
|||||||
|
|
||||||
The `code-server` script serves an HTTP API for login and starting a remote VS Code process.
|
The `code-server` script serves an HTTP API for login and starting a remote VS Code process.
|
||||||
|
|
||||||
The CLI code is in [./src/node](./src/node) and the HTTP routes are implemented in
|
The CLI code is in [src/node](../src/node) and the HTTP routes are implemented in
|
||||||
[./src/node/app](./src/node/app).
|
[src/node/routes](../src/node/routes).
|
||||||
|
|
||||||
Most of the meaty parts are in the VS Code portion of the codebase under [./lib/vscode](./lib/vscode), which we described next.
|
Most of the meaty parts are in the VS Code portion of the codebase under [lib/vscode](../lib/vscode), which we described next.
|
||||||
|
|
||||||
### Modifications to VS Code
|
### Modifications to VS Code
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ and exposed an API to the front-end for file access and all UI needs.
|
|||||||
|
|
||||||
Over time, Microsoft added support to VS Code to run it on the web. They have made
|
Over time, Microsoft added support to VS Code to run it on the web. They have made
|
||||||
the front-end open source, but not the server. As such, code-server v2 (and later) uses
|
the front-end open source, but not the server. As such, code-server v2 (and later) uses
|
||||||
the VS Code front-end and implements the server. We do this by using a git subtree to fork and modify VS Code. This code lives under [./lib/vscode](./lib/vscode).
|
the VS Code front-end and implements the server. We do this by using a git subtree to fork and modify VS Code. This code lives under [lib/vscode](../lib/vscode).
|
||||||
|
|
||||||
Some noteworthy changes in our version of VS Code:
|
Some noteworthy changes in our version of VS Code:
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
- [Questions?](#questions)
|
- [Questions?](#questions)
|
||||||
- [iPad Status?](#ipad-status)
|
- [iPad Status?](#ipad-status)
|
||||||
- [Community projects (awesome-code-server)](#community-projects-awesome-code-server)
|
- [Community Projects (awesome-code-server)](#community-projects-awesome-code-server)
|
||||||
- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration)
|
- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration)
|
||||||
- [Differences compared to VS Code?](#differences-compared-to-vs-code)
|
- [Differences compared to VS Code?](#differences-compared-to-vs-code)
|
||||||
- [How can I request a missing extension?](#how-can-i-request-a-missing-extension)
|
- [How can I request a missing extension?](#how-can-i-request-a-missing-extension)
|
||||||
@ -43,7 +43,7 @@ Please file all questions and support requests at https://github.com/cdr/code-se
|
|||||||
|
|
||||||
Please see [./ipad.md](./ipad.md).
|
Please see [./ipad.md](./ipad.md).
|
||||||
|
|
||||||
## Community projects (awesome-code-server)
|
## Community Projects (awesome-code-server)
|
||||||
|
|
||||||
Visit the [awesome-code-server](https://github.com/cdr/awesome-code-server) repository to view community projects and guides with code-server! Feel free to add your own!
|
Visit the [awesome-code-server](https://github.com/cdr/awesome-code-server) repository to view community projects and guides with code-server! Feel free to add your own!
|
||||||
|
|
||||||
|
10
package.json
10
package.json
@ -143,8 +143,16 @@
|
|||||||
"lines": 40
|
"lines": 40
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"testTimeout": 30000,
|
||||||
|
"globalSetup": "<rootDir>/test/globalSetup.ts",
|
||||||
"modulePathIgnorePatterns": [
|
"modulePathIgnorePatterns": [
|
||||||
"<rootDir>/release"
|
"<rootDir>/lib/vscode",
|
||||||
|
"<rootDir>/release-packages",
|
||||||
|
"<rootDir>/release",
|
||||||
|
"<rootDir>/release-standalone",
|
||||||
|
"<rootDir>/release-npm-package",
|
||||||
|
"<rootDir>/release-gcp",
|
||||||
|
"<rootDir>/release-images"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
body {
|
body {
|
||||||
|
min-height: 568px;
|
||||||
|
min-width: 320px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,6 +17,12 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.login-form > .field {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.login-form > .error {
|
.login-form > .error {
|
||||||
color: red;
|
color: red;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
@ -38,6 +46,13 @@ body {
|
|||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.login-form > .field > .submit {
|
||||||
|
margin-left: 0px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { rootPath } from "../constants"
|
|||||||
import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http"
|
import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http"
|
||||||
import { hash, humanPath } from "../util"
|
import { hash, humanPath } from "../util"
|
||||||
|
|
||||||
enum Cookie {
|
export enum Cookie {
|
||||||
Key = "key",
|
Key = "key",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3
test/constants.ts
Normal file
3
test/constants.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const CODE_SERVER_ADDRESS = process.env.CODE_SERVER_ADDRESS || "http://localhost:8080"
|
||||||
|
export const PASSWORD = process.env.PASSWORD || "e45432jklfdsab"
|
||||||
|
export const STORAGE = process.env.STORAGE || ""
|
@ -1,4 +1,5 @@
|
|||||||
import { chromium, Page, Browser } from "playwright"
|
import { chromium, Page, Browser } from "playwright"
|
||||||
|
import { CODE_SERVER_ADDRESS } from "./constants"
|
||||||
|
|
||||||
let browser: Browser
|
let browser: Browser
|
||||||
let page: Page
|
let page: Page
|
||||||
@ -17,7 +18,7 @@ afterEach(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should see the login page", async () => {
|
it("should see the login page", async () => {
|
||||||
await page.goto("http://localhost:8080")
|
await page.goto(CODE_SERVER_ADDRESS)
|
||||||
// It should send us to the login page
|
// It should send us to the login page
|
||||||
expect(await page.title()).toBe("code-server login")
|
expect(await page.title()).toBe("code-server login")
|
||||||
})
|
})
|
||||||
|
34
test/globalSetup.ts
Normal file
34
test/globalSetup.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// This setup runs before our e2e tests
|
||||||
|
// so that it authenticates us into code-server
|
||||||
|
// ensuring that we're logged in before we run any tests
|
||||||
|
import { chromium } from "playwright"
|
||||||
|
import { CODE_SERVER_ADDRESS, PASSWORD } from "./constants"
|
||||||
|
import * as wtfnode from "./wtfnode"
|
||||||
|
|
||||||
|
module.exports = async () => {
|
||||||
|
console.log("\n🚨 Running Global Setup for Jest Tests")
|
||||||
|
console.log(" Please hang tight...")
|
||||||
|
const browser = await chromium.launch()
|
||||||
|
const context = await browser.newContext()
|
||||||
|
const page = await context.newPage()
|
||||||
|
|
||||||
|
if (process.env.WTF_NODE) {
|
||||||
|
wtfnode.setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.goto(CODE_SERVER_ADDRESS, { waitUntil: "domcontentloaded" })
|
||||||
|
// Type in password
|
||||||
|
await page.fill(".password", PASSWORD)
|
||||||
|
// Click the submit button and login
|
||||||
|
await page.click(".submit")
|
||||||
|
|
||||||
|
// Save storage state and store as an env variable
|
||||||
|
// More info: https://playwright.dev/docs/auth?_highlight=authe#reuse-authentication-state
|
||||||
|
const storage = await context.storageState()
|
||||||
|
process.env.STORAGE = JSON.stringify(storage)
|
||||||
|
|
||||||
|
await page.close()
|
||||||
|
await browser.close()
|
||||||
|
await context.close()
|
||||||
|
console.log("✅ Global Setup for Jest Tests is now complete.")
|
||||||
|
}
|
88
test/goHome.test.ts
Normal file
88
test/goHome.test.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { chromium, Page, Browser, BrowserContext, Cookie } from "playwright"
|
||||||
|
import { hash } from "../src/node/util"
|
||||||
|
import { CODE_SERVER_ADDRESS, PASSWORD, STORAGE } from "./constants"
|
||||||
|
import { createCookieIfDoesntExist } from "./helpers"
|
||||||
|
|
||||||
|
describe("go home", () => {
|
||||||
|
let browser: Browser
|
||||||
|
let page: Page
|
||||||
|
let context: BrowserContext
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
browser = await chromium.launch()
|
||||||
|
// Create a new context with the saved storage state
|
||||||
|
const storageState = JSON.parse(STORAGE) || {}
|
||||||
|
|
||||||
|
const cookieToStore = {
|
||||||
|
sameSite: "Lax" as const,
|
||||||
|
name: "key",
|
||||||
|
value: hash(PASSWORD),
|
||||||
|
domain: "localhost",
|
||||||
|
path: "/",
|
||||||
|
expires: -1,
|
||||||
|
httpOnly: false,
|
||||||
|
secure: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// For some odd reason, the login method used in globalSetup.ts doesn't always work
|
||||||
|
// I don't know if it's on playwright clearing our cookies by accident
|
||||||
|
// or if it's our cookies disappearing.
|
||||||
|
// This means we need an additional check to make sure we're logged in.
|
||||||
|
// We do this by manually adding the cookie to the browser environment
|
||||||
|
// if it's not there at the time the test starts
|
||||||
|
const cookies: Cookie[] = storageState.cookies || []
|
||||||
|
// If the cookie exists in cookies then
|
||||||
|
// this will return the cookies with no changes
|
||||||
|
// otherwise if it doesn't exist, it will create it
|
||||||
|
// hence the name maybeUpdatedCookies
|
||||||
|
//
|
||||||
|
// TODO(@jsjoeio)
|
||||||
|
// The playwright storage thing sometimes works and sometimes doesn't. We should investigate this further
|
||||||
|
// at some point.
|
||||||
|
// See discussion: https://github.com/cdr/code-server/pull/2648#discussion_r575434946
|
||||||
|
|
||||||
|
const maybeUpdatedCookies = createCookieIfDoesntExist(cookies, cookieToStore)
|
||||||
|
|
||||||
|
context = await browser.newContext({
|
||||||
|
storageState: { cookies: maybeUpdatedCookies },
|
||||||
|
recordVideo: { dir: "./test/videos/" },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// Remove password from local storage
|
||||||
|
await context.clearCookies()
|
||||||
|
|
||||||
|
await context.close()
|
||||||
|
await browser.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
page = await context.newPage()
|
||||||
|
})
|
||||||
|
|
||||||
|
// NOTE: this test will fail if you do not run code-server with --home $CODE_SERVER_ADDRESS/healthz
|
||||||
|
it("should see a 'Go Home' button in the Application Menu that goes to /healthz", async () => {
|
||||||
|
const GO_HOME_URL = `${CODE_SERVER_ADDRESS}/healthz`
|
||||||
|
// Sometimes a dialog shows up when you navigate
|
||||||
|
// asking if you're sure you want to leave
|
||||||
|
// so we listen if it comes, we accept it
|
||||||
|
page.on("dialog", (dialog) => dialog.accept())
|
||||||
|
|
||||||
|
// waitUntil: "domcontentloaded"
|
||||||
|
// In case the page takes a long time to load
|
||||||
|
await page.goto(CODE_SERVER_ADDRESS, { waitUntil: "domcontentloaded" })
|
||||||
|
|
||||||
|
// Click the Home menu
|
||||||
|
await page.click(".home-bar ul[aria-label='Home'] li")
|
||||||
|
// See the Go Home button
|
||||||
|
const goHomeButton = "a.action-menu-item span[aria-label='Go Home']"
|
||||||
|
expect(await page.isVisible(goHomeButton))
|
||||||
|
|
||||||
|
// Click it and navigate to /healthz
|
||||||
|
// NOTE: ran into issues of it failing intermittently
|
||||||
|
// without having button: "middle"
|
||||||
|
await Promise.all([page.waitForNavigation(), page.click(goHomeButton, { button: "middle" })])
|
||||||
|
expect(page.url()).toBe(GO_HOME_URL)
|
||||||
|
})
|
||||||
|
})
|
35
test/helpers.ts
Normal file
35
test/helpers.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Borrowed from playwright
|
||||||
|
export interface Cookie {
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
|
domain: string
|
||||||
|
path: string
|
||||||
|
/**
|
||||||
|
* Unix time in seconds.
|
||||||
|
*/
|
||||||
|
expires: number
|
||||||
|
httpOnly: boolean
|
||||||
|
secure: boolean
|
||||||
|
sameSite: "Strict" | "Lax" | "None"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a cookie exists in array of cookies
|
||||||
|
*/
|
||||||
|
export function checkForCookie(cookies: Array<Cookie>, key: string): boolean {
|
||||||
|
// Check for a cookie where the name is equal to key
|
||||||
|
return Boolean(cookies.find((cookie) => cookie.name === key))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a login cookie if one doesn't already exist
|
||||||
|
*/
|
||||||
|
export function createCookieIfDoesntExist(cookies: Array<Cookie>, cookieToStore: Cookie): Array<Cookie> {
|
||||||
|
const cookieName = cookieToStore.name
|
||||||
|
const doesCookieExist = checkForCookie(cookies, cookieName)
|
||||||
|
if (!doesCookieExist) {
|
||||||
|
const updatedCookies = [...cookies, cookieToStore]
|
||||||
|
return updatedCookies
|
||||||
|
}
|
||||||
|
return cookies
|
||||||
|
}
|
38
test/login.test.ts
Normal file
38
test/login.test.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { chromium, Page, Browser, BrowserContext } from "playwright"
|
||||||
|
import { CODE_SERVER_ADDRESS, PASSWORD } from "./constants"
|
||||||
|
|
||||||
|
describe("login", () => {
|
||||||
|
let browser: Browser
|
||||||
|
let page: Page
|
||||||
|
let context: BrowserContext
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
browser = await chromium.launch()
|
||||||
|
context = await browser.newContext()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await browser.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
page = await context.newPage()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await page.close()
|
||||||
|
// Remove password from local storage
|
||||||
|
await context.clearCookies()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to login", async () => {
|
||||||
|
await page.goto(CODE_SERVER_ADDRESS)
|
||||||
|
// Type in password
|
||||||
|
await page.fill(".password", PASSWORD)
|
||||||
|
// Click the submit button and login
|
||||||
|
await page.click(".submit")
|
||||||
|
// See the editor
|
||||||
|
const codeServerEditor = await page.isVisible(".monaco-workbench")
|
||||||
|
expect(codeServerEditor).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
@ -6,11 +6,8 @@ import * as tls from "tls"
|
|||||||
import { Emitter } from "../src/common/emitter"
|
import { Emitter } from "../src/common/emitter"
|
||||||
import { SocketProxyProvider } from "../src/node/socket"
|
import { SocketProxyProvider } from "../src/node/socket"
|
||||||
import { generateCertificate, tmpdir } from "../src/node/util"
|
import { generateCertificate, tmpdir } from "../src/node/util"
|
||||||
import * as wtfnode from "./wtfnode"
|
|
||||||
|
|
||||||
describe("SocketProxyProvider", () => {
|
describe("SocketProxyProvider", () => {
|
||||||
wtfnode.setup()
|
|
||||||
|
|
||||||
const provider = new SocketProxyProvider()
|
const provider = new SocketProxyProvider()
|
||||||
|
|
||||||
const onServerError = new Emitter<{ event: string; error: Error }>()
|
const onServerError = new Emitter<{ event: string; error: Error }>()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { JSDOM } from "jsdom"
|
import { JSDOM } from "jsdom"
|
||||||
|
import { Cookie } from "playwright"
|
||||||
// Note: we need to import logger from the root
|
// Note: we need to import logger from the root
|
||||||
// because this is the logger used in logError in ../src/common/util
|
// because this is the logger used in logError in ../src/common/util
|
||||||
import { logger } from "../node_modules/@coder/logger"
|
import { logger } from "../node_modules/@coder/logger"
|
||||||
@ -8,12 +9,16 @@ import {
|
|||||||
getFirstString,
|
getFirstString,
|
||||||
getOptions,
|
getOptions,
|
||||||
logError,
|
logError,
|
||||||
normalize,
|
|
||||||
plural,
|
plural,
|
||||||
resolveBase,
|
resolveBase,
|
||||||
split,
|
split,
|
||||||
trimSlashes,
|
trimSlashes,
|
||||||
|
normalize,
|
||||||
} from "../src/common/util"
|
} from "../src/common/util"
|
||||||
|
import { Cookie as CookieEnum } from "../src/node/routes/login"
|
||||||
|
import { hash } from "../src/node/util"
|
||||||
|
import { PASSWORD } from "./constants"
|
||||||
|
import { checkForCookie, createCookieIfDoesntExist } from "./helpers"
|
||||||
|
|
||||||
const dom = new JSDOM()
|
const dom = new JSDOM()
|
||||||
global.document = dom.window.document
|
global.document = dom.window.document
|
||||||
@ -255,4 +260,58 @@ describe("util", () => {
|
|||||||
expect(spy).toHaveBeenCalledWith("api: oh no")
|
expect(spy).toHaveBeenCalledWith("api: oh no")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("checkForCookie", () => {
|
||||||
|
it("should check if the cookie exists and has a value", () => {
|
||||||
|
const fakeCookies: Cookie[] = [
|
||||||
|
{
|
||||||
|
name: CookieEnum.Key,
|
||||||
|
value: hash(PASSWORD),
|
||||||
|
domain: "localhost",
|
||||||
|
secure: false,
|
||||||
|
sameSite: "Lax",
|
||||||
|
httpOnly: false,
|
||||||
|
expires: 18000,
|
||||||
|
path: "/",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
expect(checkForCookie(fakeCookies, CookieEnum.Key)).toBe(true)
|
||||||
|
})
|
||||||
|
it("should return false if there are no cookies", () => {
|
||||||
|
const fakeCookies: Cookie[] = []
|
||||||
|
expect(checkForCookie(fakeCookies, "key")).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("createCookieIfDoesntExist", () => {
|
||||||
|
it("should create a cookie if it doesn't exist", () => {
|
||||||
|
const cookies: Cookie[] = []
|
||||||
|
const cookieToStore = {
|
||||||
|
name: CookieEnum.Key,
|
||||||
|
value: hash(PASSWORD),
|
||||||
|
domain: "localhost",
|
||||||
|
secure: false,
|
||||||
|
sameSite: "Lax" as const,
|
||||||
|
httpOnly: false,
|
||||||
|
expires: 18000,
|
||||||
|
path: "/",
|
||||||
|
}
|
||||||
|
expect(createCookieIfDoesntExist(cookies, cookieToStore)).toStrictEqual([cookieToStore])
|
||||||
|
})
|
||||||
|
it("should return the same cookies if the cookie already exists", () => {
|
||||||
|
const PASSWORD = "123supersecure"
|
||||||
|
const cookieToStore = {
|
||||||
|
name: CookieEnum.Key,
|
||||||
|
value: hash(PASSWORD),
|
||||||
|
domain: "localhost",
|
||||||
|
secure: false,
|
||||||
|
sameSite: "Lax" as const,
|
||||||
|
httpOnly: false,
|
||||||
|
expires: 18000,
|
||||||
|
path: "/",
|
||||||
|
}
|
||||||
|
const cookies: Cookie[] = [cookieToStore]
|
||||||
|
expect(createCookieIfDoesntExist(cookies, cookieToStore)).toStrictEqual(cookies)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,23 @@
|
|||||||
|
import * as util from "util"
|
||||||
import * as wtfnode from "wtfnode"
|
import * as wtfnode from "wtfnode"
|
||||||
|
|
||||||
|
// Jest seems to hijack console.log in a way that makes the output difficult to
|
||||||
|
// read. So we'll write directly to process.stderr instead.
|
||||||
|
const write = (...args: [any, ...any]) => {
|
||||||
|
if (args.length > 0) {
|
||||||
|
process.stderr.write(util.format(...args) + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wtfnode.setLogger("info", write)
|
||||||
|
wtfnode.setLogger("warn", write)
|
||||||
|
wtfnode.setLogger("error", write)
|
||||||
|
|
||||||
let active = false
|
let active = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start logging open handles periodically. This can be used to see what is
|
||||||
|
* hanging open if anything.
|
||||||
|
*/
|
||||||
export function setup(): void {
|
export function setup(): void {
|
||||||
if (active) {
|
if (active) {
|
||||||
return
|
return
|
||||||
|
Reference in New Issue
Block a user