Skip to content

Commit 86994da

Browse files
authored
feat: Initial E2E test framework for v2 (coder#288)
This brings an initial E2E test (really, an integration test - it's only running the server locally, as opposed to against a deployment - but it'd be easy to point playwright to a deployment). Demo gif: ![test2](https://user-images.githubusercontent.com/88213859/156078517-6cb4ef84-337b-4e16-a8bc-aea7d06dcbcb.gif) This test exercises a minimal flow for login: - Run the `coderd` binary to start a server on 3000 - Create an initial user as part of setup - Go through the login flow and verify we land on the projects page It will be useful to have to ensure that coder#360 doesn't introduce a regression in the login flow Future E2E tests that would be useful: - Create a project & verify it shows in the UI - Create a workspace and verify it shows in the UI
1 parent 2b0f471 commit 86994da

File tree

16 files changed

+559
-40
lines changed

16 files changed

+559
-40
lines changed

.github/workflows/coder.yaml

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ jobs:
164164
env:
165165
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
166166
DD_DATABASE: fake
167+
DD_CATEGORY: unit
167168
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
168169
run: go run scripts/datadog-cireport/main.go gotests.xml
169170

@@ -279,6 +280,74 @@ jobs:
279280
if: (success() || failure()) && github.actor != 'dependabot[bot]'
280281
env:
281282
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
282-
DD_DATABASE: postgresql
283+
DD_CATEGORY: unit
284+
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
285+
run: go run scripts/datadog-cireport/main.go site/test-results/junit.xml
286+
287+
test-e2e:
288+
name: "test/e2e/${{ matrix.os }}"
289+
runs-on: ${{ matrix.os }}
290+
strategy:
291+
matrix:
292+
os:
293+
- ubuntu-latest
294+
- macos-latest
295+
# TODO: Get `make build` running on Windows 2022
296+
# https://github.com/coder/coder/issues/384
297+
# - windows-2022
298+
steps:
299+
- uses: actions/checkout@v2
300+
301+
- name: Cache Node
302+
id: cache-node
303+
uses: actions/cache@v2
304+
with:
305+
path: |
306+
**/node_modules
307+
.eslintcache
308+
key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
309+
310+
# Go is required for uploading the test results to datadog
311+
- uses: actions/setup-go@v2
312+
with:
313+
go-version: "^1.17"
314+
315+
- uses: hashicorp/setup-terraform@v1
316+
with:
317+
terraform_version: 1.1.2
318+
terraform_wrapper: false
319+
320+
- uses: actions/setup-node@v3
321+
with:
322+
node-version: "14"
323+
324+
- uses: actions/cache@v2
325+
with:
326+
# Go mod cache, Linux build cache, Mac build cache, Windows build cache
327+
path: |
328+
~/go/pkg/mod
329+
~/.cache/go-build
330+
~/Library/Caches/go-build
331+
%LocalAppData%\go-build
332+
key: ${{ matrix.os }}-go-${{ hashFiles('**/go.sum') }}
333+
restore-keys: |
334+
${{ matrix.os }}-go-
335+
336+
- run: make build
337+
338+
- run: yarn playwright:install
339+
working-directory: site
340+
341+
- run: yarn playwright:install-deps
342+
working-directory: site
343+
344+
- run: yarn playwright:test
345+
working-directory: site
346+
347+
- name: Upload DataDog Trace
348+
if: (success() || failure()) && github.actor != 'dependabot[bot]' && runner.os == 'Linux'
349+
env:
350+
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
351+
DD_CATEGORY: e2e
283352
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
284-
run: go run scripts/datadog-cireport/main.go site/test_results/junit.xml
353+
run: go run scripts/datadog-cireport/main.go site/test-results/junit.xml

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ site/.eslintcache
2020
site/.next/
2121
site/node_modules/
2222
site/storybook-static/
23-
site/test_results/
23+
site/test-results/
2424
site/yarn-error.log
2525
coverage/
2626

scripts/datadog-cireport/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ func main() {
7575
"service": "coder",
7676
"_dd.cireport_version": "2",
7777

78-
"test.traits": fmt.Sprintf(`{"database":["%s"]}`, os.Getenv("DD_DATABASE")),
78+
"test.traits": fmt.Sprintf(`{"database":["%s"], "category":["%s"]}`,
79+
os.Getenv("DD_DATABASE"), os.Getenv("DD_CATEGORY")),
7980

8081
// Additional tags found in DataDog docs. See:
8182
// https://docs.datadoghq.com/continuous_integration/setup_tests/junit_upload/#collecting-environment-configuration-metadata

site/.eslintignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ out
77
coverage
88
.next
99
storybook-static
10-
test_results
10+
test-results

site/.prettierignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ yarn-error.log
1515
coverage/
1616
out/
1717
storybook-static/
18-
test_results/
18+
test-results/

site/e2e/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Default credentials and user for running tests
2+
export const username = "admin"
3+
export const password = "password"
4+
export const organization = "acme-crop"
5+
export const email = "admin@coder.com"

site/e2e/globalSetup.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { FullConfig, request } from "@playwright/test"
2+
import { email, username, password, organization } from "./constants"
3+
4+
const globalSetup = async (config: FullConfig): Promise<void> => {
5+
// Grab the 'baseURL' from the webserver (`coderd`)
6+
const { baseURL } = config.projects[0].use
7+
8+
// Create a context that will issue http requests.
9+
const context = await request.newContext({
10+
baseURL,
11+
})
12+
13+
// Create initial user
14+
await context.post("/api/v2/user", {
15+
data: {
16+
email,
17+
username,
18+
password,
19+
organization,
20+
},
21+
})
22+
}
23+
24+
export default globalSetup

site/e2e/playwright.config.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as path from "path"
2+
import { PlaywrightTestConfig } from "@playwright/test"
3+
4+
const config: PlaywrightTestConfig = {
5+
testDir: "tests",
6+
globalSetup: require.resolve("./globalSetup"),
7+
8+
// Create junit report file for upload to DataDog
9+
reporter: [["junit", { outputFile: "test-results/junit.xml" }]],
10+
11+
use: {
12+
baseURL: "http://localhost:3000",
13+
video: "retain-on-failure",
14+
},
15+
16+
// `webServer` tells Playwright to launch a test server - more details here:
17+
// https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests
18+
webServer: {
19+
// Run the 'coderd' binary directly
20+
command: path.join(__dirname, "../../bin/coderd"),
21+
port: 3000,
22+
timeout: 120 * 10000,
23+
reuseExistingServer: false,
24+
},
25+
}
26+
27+
export default config

site/e2e/pom/BasePom.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Page } from "@playwright/test"
2+
3+
export abstract class BasePom {
4+
protected readonly baseURL: string | undefined
5+
protected readonly path: string
6+
protected readonly page: Page
7+
8+
constructor(baseURL: string | undefined, path: string, page: Page) {
9+
this.baseURL = baseURL
10+
this.path = path
11+
this.page = page
12+
}
13+
14+
get url(): string {
15+
return this.baseURL + this.path
16+
}
17+
}

site/e2e/pom/ProjectsPage.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Page } from "@playwright/test"
2+
import { BasePom } from "./BasePom"
3+
4+
export class ProjectsPage extends BasePom {
5+
constructor(baseURL: string | undefined, page: Page) {
6+
super(baseURL, "/projects", page)
7+
}
8+
}

site/e2e/pom/SignInPage.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Page } from "@playwright/test"
2+
import { BasePom } from "./BasePom"
3+
4+
export class SignInPage extends BasePom {
5+
constructor(baseURL: string | undefined, page: Page) {
6+
super(baseURL, "/login", page)
7+
}
8+
9+
async submitBuiltInAuthentication(email: string, password: string): Promise<void> {
10+
await this.page.fill("id=signin-form-inpt-email", email)
11+
await this.page.fill("id=signin-form-inpt-password", password)
12+
await this.page.click("id=signin-form-submit")
13+
}
14+
}

site/e2e/pom/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./ProjectsPage"
2+
export * from "./SignInPage"

site/e2e/tests/login.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { test } from "@playwright/test"
2+
import { ProjectsPage, SignInPage } from "../pom"
3+
import { email, password } from "../constants"
4+
5+
test("Login takes user to /projects", async ({ baseURL, page }) => {
6+
await page.goto(baseURL + "/", { waitUntil: "networkidle" })
7+
8+
// Log-in with the default credentials we set up in the development server
9+
const signInPage = new SignInPage(baseURL, page)
10+
await signInPage.submitBuiltInAuthentication(email, password)
11+
12+
const projectsPage = new ProjectsPage(baseURL, page)
13+
await page.waitForNavigation({ url: projectsPage.url, waitUntil: "networkidle" })
14+
15+
await page.waitForSelector("text=Projects")
16+
})

site/jest.config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module.exports = {
1717
},
1818
testEnvironment: "jsdom",
1919
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
20-
testPathIgnorePatterns: ["/node_modules/", "/__tests__/fakes"],
20+
testPathIgnorePatterns: ["/node_modules/", "/__tests__/fakes", "/e2e/"],
2121
moduleDirectories: ["node_modules", "<rootDir>"],
2222
},
2323
{
@@ -45,6 +45,7 @@ module.exports = {
4545
"!<rootDir>/api.ts",
4646
"!<rootDir>/coverage/**/*.*",
4747
"!<rootDir>/dev.ts",
48+
"!<rootDir>/e2e/**/*.*",
4849
"!<rootDir>/jest-runner.eslint.config.js",
4950
"!<rootDir>/jest.config.js",
5051
"!<rootDir>/next-env.d.ts",
@@ -58,7 +59,7 @@ module.exports = {
5859
"jest-junit",
5960
{
6061
suiteName: "Front-end Jest Tests",
61-
outputDirectory: "./test_results",
62+
outputDirectory: "./test-results",
6263
outputName: "junit.xml",
6364
},
6465
],

site/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
"format:write": "prettier --write '**/*.{css,html,js,json,jsx,md,ts,tsx,yaml,yml}' && sql-formatter -l postgresql ./database/query.sql -o ./database/query.sql",
1313
"lint": "jest --selectProjects lint",
1414
"lint:fix": "FIX=true yarn lint",
15+
"playwright:install": "playwright install",
16+
"playwright:install-deps": "playwright install-deps",
17+
"playwright:test": "playwright test --config=e2e/playwright.config.ts",
1518
"storybook": "start-storybook -p 6006 -s ./static",
1619
"storybook:build": "build-storybook",
1720
"test": "jest --selectProjects test",
@@ -21,6 +24,7 @@
2124
"@material-ui/core": "4.9.4",
2225
"@material-ui/icons": "4.5.1",
2326
"@material-ui/lab": "4.0.0-alpha.42",
27+
"@playwright/test": "1.17.2",
2428
"@react-theming/storybook-addon": "1.1.5",
2529
"@storybook/addon-actions": "6.4.19",
2630
"@storybook/addon-essentials": "6.4.19",

0 commit comments

Comments
 (0)