Skip to content

Commit da4de43

Browse files
committed
Spawn a code-server instance for each test suite
This uses the current dev build by default but can be overidden with CODE_SERVER_TEST_ENTRY (for example to test a release or some other version). Each instance has a separate state directory. This should make parallelization work. This also means you are no longer required to specify the password and address yourself (or the extension directory once we add a test extension). `yarn test:e2e` should just work as-is. Lastly, it means the tests are no longer subject to yarn watch randomly restarting.
1 parent 49c4481 commit da4de43

14 files changed

+251
-70
lines changed

.github/workflows/ci.yaml

+8-7
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,9 @@ jobs:
334334
needs: package-linux-amd64
335335
runs-on: ubuntu-latest
336336
env:
337-
PASSWORD: e45432jklfdsab
338-
CODE_SERVER_ADDRESS: http://localhost:8080
337+
# Since we build code-server we might as well run tests from the release
338+
# since VS Code will load faster due to the bundling.
339+
CODE_SERVER_TEST_ENTRY: "./release-packages/code-server-linux-amd64"
339340
steps:
340341
- uses: actions/checkout@v2
341342

@@ -362,9 +363,11 @@ jobs:
362363
name: release-packages
363364
path: ./release-packages
364365

365-
- name: Untar code-server file
366+
- name: Untar code-server release
366367
run: |
367-
cd release-packages && tar -xzf code-server*-linux-amd64.tar.gz
368+
cd release-packages
369+
tar -xzf code-server*-linux-amd64.tar.gz
370+
mv code-server*-linux-amd64 code-server-linux-amd64
368371
369372
- name: Install dependencies
370373
if: steps.cache-yarn.outputs.cache-hit != 'true'
@@ -380,9 +383,7 @@ jobs:
380383
yarn install --check-files
381384
382385
- name: Run end-to-end tests
383-
run: |
384-
./release-packages/code-server*-linux-amd64/bin/code-server --log trace &
385-
yarn test:e2e
386+
run: yarn test:e2e
386387

387388
- name: Upload test artifacts
388389
if: always()

ci/dev/test-e2e.sh

+28-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,35 @@ set -euo pipefail
33

44
main() {
55
cd "$(dirname "$0")/../.."
6+
source ./ci/lib.sh
7+
8+
local dir="$PWD"
9+
if [[ ! ${CODE_SERVER_TEST_ENTRY-} ]]; then
10+
echo "Set CODE_SERVER_TEST_ENTRY to test another build of code-server"
11+
else
12+
pushd "$CODE_SERVER_TEST_ENTRY"
13+
dir="$PWD"
14+
popd
15+
fi
16+
17+
echo "Testing build in '$dir'"
18+
19+
# Simple sanity checks to see that we've built. There could still be things
20+
# wrong (native modules version issues, incomplete build, etc).
21+
if [[ ! -d $dir/out ]]; then
22+
echo >&2 "No code-server build detected"
23+
echo >&2 "You can build it with 'yarn build' or 'yarn watch'"
24+
exit 1
25+
fi
26+
27+
if [[ ! -d $dir/lib/vscode/out ]]; then
28+
echo >&2 "No VS Code build detected"
29+
echo >&2 "You can build it with 'yarn build:vscode' or 'yarn watch'"
30+
exit 1
31+
fi
32+
633
cd test
7-
# We set these environment variables because they're used in the e2e tests
8-
# they don't have to be these values, but these are the defaults
9-
PASSWORD=e45432jklfdsab CODE_SERVER_ADDRESS=http://localhost:8080 yarn playwright test "$@"
34+
yarn playwright test "$@"
1035
}
1136

1237
main "$@"

test/e2e/baseFixture.ts

+41-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,47 @@
1+
import { field, logger } from "@coder/logger"
12
import { test as base } from "@playwright/test"
2-
import { CodeServer } from "./models/CodeServer"
3+
import { CodeServer, CodeServerPage } from "./models/CodeServer"
34

4-
export const test = base.extend<{ codeServerPage: CodeServer }>({
5-
codeServerPage: async ({ page }, use) => {
6-
const codeServer = new CodeServer(page)
7-
await codeServer.navigate()
8-
await use(codeServer)
5+
/**
6+
* Wraps `test.describe` to create and manage an instance of code-server. If you
7+
* don't use this you will need to create your own code-server instance and pass
8+
* it to `test.use`.
9+
*/
10+
export const describe = (name: string, fn: (codeServer: CodeServer) => void) => {
11+
test.describe(name, () => {
12+
// This will spawn on demand so nothing is necessary on before.
13+
const codeServer = new CodeServer(name)
14+
15+
// Kill code-server after the suite has ended. This may happen even without
16+
// doing it explicitly but it seems prudent to be sure.
17+
test.afterAll(async () => {
18+
await codeServer.close()
19+
})
20+
21+
// This makes `codeServer` available to the extend call below.
22+
test.use({ codeServer })
23+
24+
fn(codeServer)
25+
})
26+
}
27+
28+
interface TestFixtures {
29+
codeServer: CodeServer
30+
codeServerPage: CodeServerPage
31+
}
32+
33+
/**
34+
* Create a test that spawns code-server if necessary and ensures the page is
35+
* ready.
36+
*/
37+
export const test = base.extend<TestFixtures>({
38+
codeServer: undefined, // No default; should be provided through `test.use`.
39+
codeServerPage: async ({ codeServer, page }, use) => {
40+
const codeServerPage = new CodeServerPage(codeServer, page)
41+
await codeServerPage.navigate()
42+
await use(codeServerPage)
943
},
1044
})
1145

46+
/** Shorthand for test.expect. */
1247
export const expect = test.expect

test/e2e/browser.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { test, expect } from "./baseFixture"
1+
import { describe, test, expect } from "./baseFixture"
22

33
// This is a "gut-check" test to make sure playwright is working as expected
4-
test.describe("browser", () => {
4+
describe("browser", () => {
55
test("browser should display correct userAgent", async ({ codeServerPage, browserName }) => {
66
const displayNames = {
77
chromium: "Chrome",

test/e2e/codeServer.test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
import { CODE_SERVER_ADDRESS, storageState } from "../utils/constants"
2-
import { test, expect } from "./baseFixture"
1+
import { storageState } from "../utils/constants"
2+
import { describe, test, expect } from "./baseFixture"
33

4-
test.describe("CodeServer", () => {
4+
describe("CodeServer", () => {
55
test.use({
66
storageState,
77
})
88

9-
test(`should navigate to ${CODE_SERVER_ADDRESS}`, async ({ codeServerPage }) => {
9+
test("should navigate to home page", async ({ codeServerPage }) => {
1010
// We navigate codeServer before each test
1111
// and we start the test with a storage state
1212
// which means we should be logged in
1313
// so it should be on the address
1414
const url = codeServerPage.page.url()
1515
// We use match because there may be a / at the end
1616
// so we don't want it to fail if we expect http://localhost:8080 to match http://localhost:8080/
17-
expect(url).toMatch(CODE_SERVER_ADDRESS)
17+
expect(url).toMatch(await codeServerPage.address())
1818
})
1919

2020
test("should always see the code-server editor", async ({ codeServerPage }) => {

test/e2e/globalSetup.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { storageState } from "../utils/constants"
2-
import { test, expect } from "./baseFixture"
2+
import { describe, test, expect } from "./baseFixture"
33

44
// This test is to make sure the globalSetup works as expected
55
// meaning globalSetup ran and stored the storageState
6-
test.describe("globalSetup", () => {
6+
describe("globalSetup", () => {
77
test.use({
88
storageState,
99
})

test/e2e/login.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { PASSWORD } from "../utils/constants"
2-
import { test, expect } from "./baseFixture"
2+
import { describe, test, expect } from "./baseFixture"
33

4-
test.describe("login", () => {
4+
describe("login", () => {
55
// Reset the browser so no cookies are persisted
66
// by emptying the storageState
77
test.use({

test/e2e/logout.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { CODE_SERVER_ADDRESS, PASSWORD } from "../utils/constants"
2-
import { test, expect } from "./baseFixture"
1+
import { PASSWORD } from "../utils/constants"
2+
import { describe, test, expect } from "./baseFixture"
33

4-
test.describe("logout", () => {
4+
describe("logout", () => {
55
// Reset the browser so no cookies are persisted
66
// by emptying the storageState
77
test.use({
@@ -39,6 +39,6 @@ test.describe("logout", () => {
3939
// https://github.com/microsoft/playwright/issues/1987#issuecomment-620182151
4040
await Promise.all([codeServerPage.page.waitForNavigation(), codeServerPage.page.click(logoutButton)])
4141
const currentUrl = codeServerPage.page.url()
42-
expect(currentUrl).toBe(`${CODE_SERVER_ADDRESS}/login`)
42+
expect(currentUrl).toBe(`${await codeServerPage.address()}/login`)
4343
})
4444
})

0 commit comments

Comments
 (0)