Skip to content

Commit 3f2411a

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. `yarn test:e2e` should just work as-is. Lastly, it means the tests are no longer subject to yarn watch randomly restarting.
1 parent 774d29a commit 3f2411a

14 files changed

+232
-67
lines changed

.github/workflows/ci.yaml

Lines changed: 4 additions & 5 deletions
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/bin/code-server"
339340
steps:
340341
- uses: actions/checkout@v2
341342

@@ -380,9 +381,7 @@ jobs:
380381
yarn install --check-files
381382
382383
- name: Run end-to-end tests
383-
run: |
384-
./release-packages/code-server*-linux-amd64/bin/code-server --log trace &
385-
yarn test:e2e
384+
run: yarn test:e2e
386385

387386
- name: Upload test artifacts
388387
if: always()

ci/dev/test-e2e.sh

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,30 @@ set -euo pipefail
33

44
main() {
55
cd "$(dirname "$0")/../.."
6+
7+
# Simple sanity checks to see that we've built. There could still be things
8+
# wrong (native modules version issues, incomplete build, etc).
9+
if [[ ! -d ./out ]]; then
10+
echo >&2 "code-server has not been built"
11+
echo >&2 "You can build it with 'yarn build' or 'yarn watch'"
12+
exit 1
13+
fi
14+
15+
if [[ ! -d ./lib/vscode/out ]]; then
16+
echo >&2 "VS Code has not been built"
17+
echo >&2 "You can build it with 'yarn build:vscode' or 'yarn watch'"
18+
exit 1
19+
fi
20+
21+
if [[ ! ${CODE_SERVER_TEST_ENTRY-} ]]; then
22+
echo "Testing current dev build"
23+
echo "Set CODE_SERVER_TEST_ENTRY to test another build of code-server"
24+
else
25+
echo "Testing build in '$CODE_SERVER_TEST_ENTRY'"
26+
fi
27+
628
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 "$@"
29+
yarn playwright test "$@"
1030
}
1131

1232
main "$@"

test/e2e/baseFixture.ts

Lines changed: 41 additions & 6 deletions
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(() => {
18+
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.setup()
42+
await use(codeServerPage)
943
},
1044
})
1145

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

test/e2e/browser.test.ts

Lines changed: 2 additions & 2 deletions
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

Lines changed: 5 additions & 5 deletions
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(codeServerPage.address)
1818
})
1919

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

test/e2e/globalSetup.test.ts

Lines changed: 2 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 4 additions & 4 deletions
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(`${codeServerPage.address}/login`)
4343
})
4444
})

0 commit comments

Comments
 (0)