-
Notifications
You must be signed in to change notification settings - Fork 905
feat: Initial E2E test framework for v2 #288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
470d06d
ab81ef9
7c970b5
853a2b9
2b0f0cb
78ceb9c
5243dea
5eb9314
8fc553a
1b0e0d3
f39f59a
0428a62
94c7684
dfe0c7c
8af7d02
18f21da
bcef4ac
91bda34
08dc699
b8cc80f
a88a722
4bad40e
3b0b4cd
15a9a11
081c9cf
439c13b
d7f9257
5eedfbe
7381ca2
d3c8e50
e66c685
bee5e25
66b7d60
7c568de
41a4516
7997a16
fae6b3f
787213c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -164,6 +164,7 @@ jobs: | |
env: | ||
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} | ||
DD_DATABASE: fake | ||
DD_CATEGORY: unit | ||
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} | ||
run: go run scripts/datadog-cireport/main.go gotests.xml | ||
|
||
|
@@ -279,6 +280,74 @@ jobs: | |
if: (success() || failure()) && github.actor != 'dependabot[bot]' | ||
env: | ||
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} | ||
DD_DATABASE: postgresql | ||
DD_CATEGORY: unit | ||
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} | ||
run: go run scripts/datadog-cireport/main.go site/test-results/junit.xml | ||
|
||
test-e2e: | ||
name: "test/e2e/${{ matrix.os }}" | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
os: | ||
- ubuntu-latest | ||
- macos-latest | ||
# TODO: Get `make build` running on Windows 2022 | ||
# https://github.com/coder/coder/issues/384 | ||
# - windows-2022 | ||
steps: | ||
- uses: actions/checkout@v2 | ||
|
||
- name: Cache Node | ||
id: cache-node | ||
uses: actions/cache@v2 | ||
with: | ||
path: | | ||
**/node_modules | ||
.eslintcache | ||
key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }} | ||
|
||
# Go is required for uploading the test results to datadog | ||
- uses: actions/setup-go@v2 | ||
with: | ||
go-version: "^1.17" | ||
|
||
- uses: hashicorp/setup-terraform@v1 | ||
with: | ||
terraform_version: 1.1.2 | ||
terraform_wrapper: false | ||
|
||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: "14" | ||
|
||
- uses: actions/cache@v2 | ||
with: | ||
# Go mod cache, Linux build cache, Mac build cache, Windows build cache | ||
path: | | ||
~/go/pkg/mod | ||
~/.cache/go-build | ||
~/Library/Caches/go-build | ||
%LocalAppData%\go-build | ||
key: ${{ matrix.os }}-go-${{ hashFiles('**/go.sum') }} | ||
restore-keys: | | ||
${{ matrix.os }}-go- | ||
|
||
- run: make build | ||
|
||
- run: yarn playwright:install | ||
working-directory: site | ||
|
||
- run: yarn playwright:install-deps | ||
working-directory: site | ||
|
||
- run: yarn playwright:test | ||
working-directory: site | ||
|
||
- name: Upload DataDog Trace | ||
if: (success() || failure()) && github.actor != 'dependabot[bot]' && runner.os == 'Linux' | ||
env: | ||
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} | ||
DD_CATEGORY: e2e | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kylecarbs - I added a new DataDog trait for test runs ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is awesome! |
||
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} | ||
run: go run scripts/datadog-cireport/main.go site/test_results/junit.xml | ||
run: go run scripts/datadog-cireport/main.go site/test-results/junit.xml |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,4 @@ out | |
coverage | ||
.next | ||
storybook-static | ||
test_results | ||
test-results |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,4 +15,4 @@ yarn-error.log | |
coverage/ | ||
out/ | ||
storybook-static/ | ||
test_results/ | ||
test-results/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Default credentials and user for running tests | ||
export const username = "admin" | ||
export const password = "password" | ||
export const organization = "acme-crop" | ||
export const email = "admin@coder.com" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bryphe-coder thanks for implementing this! 🎉 I def didn't mean for it to block the work, but it's a nice touch 🎊 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the great feedback & review @vapurrmaid ! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { FullConfig, request } from "@playwright/test" | ||
import { email, username, password, organization } from "./constants" | ||
|
||
const globalSetup = async (config: FullConfig): Promise<void> => { | ||
// Grab the 'baseURL' from the webserver (`coderd`) | ||
const { baseURL } = config.projects[0].use | ||
|
||
// Create a context that will issue http requests. | ||
const context = await request.newContext({ | ||
baseURL, | ||
}) | ||
|
||
// Create initial user | ||
await context.post("/api/v2/user", { | ||
data: { | ||
email, | ||
username, | ||
password, | ||
organization, | ||
}, | ||
}) | ||
} | ||
|
||
export default globalSetup |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import * as path from "path" | ||
import { PlaywrightTestConfig } from "@playwright/test" | ||
|
||
const config: PlaywrightTestConfig = { | ||
testDir: "tests", | ||
globalSetup: require.resolve("./globalSetup"), | ||
|
||
// Create junit report file for upload to DataDog | ||
reporter: [["junit", { outputFile: "test-results/junit.xml" }]], | ||
|
||
use: { | ||
baseURL: "http://localhost:3000", | ||
video: "retain-on-failure", | ||
}, | ||
|
||
// `webServer` tells Playwright to launch a test server - more details here: | ||
// https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests | ||
webServer: { | ||
// Run the 'coderd' binary directly | ||
command: path.join(__dirname, "../../bin/coderd"), | ||
port: 3000, | ||
timeout: 120 * 10000, | ||
reuseExistingServer: false, | ||
}, | ||
} | ||
|
||
export default config |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Page } from "@playwright/test" | ||
|
||
export abstract class BasePom { | ||
protected readonly baseURL: string | undefined | ||
protected readonly path: string | ||
protected readonly page: Page | ||
|
||
constructor(baseURL: string | undefined, path: string, page: Page) { | ||
this.baseURL = baseURL | ||
this.path = path | ||
this.page = page | ||
} | ||
|
||
get url(): string { | ||
return this.baseURL + this.path | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { Page } from "@playwright/test" | ||
import { BasePom } from "./BasePom" | ||
|
||
export class ProjectsPage extends BasePom { | ||
constructor(baseURL: string | undefined, page: Page) { | ||
super(baseURL, "/projects", page) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Page } from "@playwright/test" | ||
import { BasePom } from "./BasePom" | ||
|
||
export class SignInPage extends BasePom { | ||
constructor(baseURL: string | undefined, page: Page) { | ||
super(baseURL, "/login", page) | ||
} | ||
|
||
async submitBuiltInAuthentication(email: string, password: string): Promise<void> { | ||
await this.page.fill("id=signin-form-inpt-email", email) | ||
await this.page.fill("id=signin-form-inpt-password", password) | ||
await this.page.click("id=signin-form-submit") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./ProjectsPage" | ||
export * from "./SignInPage" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { test } from "@playwright/test" | ||
import { ProjectsPage, SignInPage } from "../pom" | ||
import { email, password } from "../constants" | ||
|
||
test("Login takes user to /projects", async ({ baseURL, page }) => { | ||
await page.goto(baseURL + "/", { waitUntil: "networkidle" }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting that unpacking the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep! The v1 E2E tests use a special (external) address to point to the server under test (picked up from With these tests - using the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Good point - we have some more utilities in the POM layer in v1, I brought them over (ie, It's a little bit awkward - since I'm passing both the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented @vapurrmaid 's suggestions which help improve this in: fae6b3f - I'll merge what we have so the test is in place for the NextJS -> Webpack changes, but happy to iterate on it more There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bryphe-coder I'm definitely in favor of this merging sooner than later for iteration, it's in a really good starting spot! |
||
|
||
// Log-in with the default credentials we set up in the development server | ||
const signInPage = new SignInPage(baseURL, page) | ||
await signInPage.submitBuiltInAuthentication(email, password) | ||
|
||
const projectsPage = new ProjectsPage(baseURL, page) | ||
await page.waitForNavigation({ url: projectsPage.url, waitUntil: "networkidle" }) | ||
|
||
await page.waitForSelector("text=Projects") | ||
}) |
Uh oh!
There was an error while loading. Please reload this page.