-
Notifications
You must be signed in to change notification settings - Fork 6.1k
chore: upgrade to Playwright 1.12 with its new test-runner #3590
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
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 |
---|---|---|
|
@@ -165,8 +165,7 @@ export const isHashLegacyMatch = (password: string, hashPassword: string) => { | |
return safeCompare(hashedWithLegacy, hashPassword) | ||
} | ||
|
||
const passwordMethods = ["SHA256", "ARGON2", "PLAIN_TEXT"] as const | ||
export type PasswordMethod = typeof passwordMethods[number] | ||
export type PasswordMethod = "SHA256" | "ARGON2" | "PLAIN_TEXT" | ||
|
||
/** | ||
* Used to determine the password method. | ||
|
@@ -413,7 +412,7 @@ export const isObject = <T extends object>(obj: T): obj is T => { | |
* we don't have to set up a `vs` alias to be able to import with types (since | ||
* the alternative is to directly import from `out`). | ||
*/ | ||
const enum CharCode { | ||
enum CharCode { | ||
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. Playwright's test-runner uses babel-plugin-transform-typescript which does not support const enums. See here. 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. Awesome! 🎉 |
||
Slash = 47, | ||
A = 65, | ||
Z = 90, | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { test as base } from "@playwright/test" | ||
import { CodeServer } from "./models/CodeServer" | ||
|
||
export const test = base.extend<{ codeServerPage: CodeServer }>({ | ||
codeServerPage: async ({ page }, use) => { | ||
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 a custom test-fixture. This means for every test a new one gets created. Since its using the page, which uses a context, Playwright does automatically take care of closing the context. 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. Woah, that is super cool! That's fantastic! 🎉 |
||
const codeServer = new CodeServer(page) | ||
await codeServer.navigate() | ||
await use(codeServer) | ||
}, | ||
}) | ||
|
||
export const expect = test.expect |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,32 @@ | ||
import { test, expect } from "@playwright/test" | ||
import { CODE_SERVER_ADDRESS, STORAGE } from "../utils/constants" | ||
import { CodeServer } from "./models/CodeServer" | ||
import { CODE_SERVER_ADDRESS, storageState } from "../utils/constants" | ||
import { test, expect } from "./baseFixture" | ||
|
||
test.describe("CodeServer", () => { | ||
// Create a new context with the saved storage state | ||
// so we don't have to logged in | ||
const options: any = {} | ||
let codeServer: CodeServer | ||
|
||
// TODO@jsjoeio | ||
// Fix this once https://github.com/microsoft/playwright-test/issues/240 | ||
// is fixed | ||
if (STORAGE) { | ||
const storageState = JSON.parse(STORAGE) || {} | ||
options.contextOptions = { | ||
storageState, | ||
} | ||
} | ||
|
||
test.beforeEach(async ({ page }) => { | ||
codeServer = new CodeServer(page) | ||
await codeServer.setup() | ||
test.use({ | ||
storageState, | ||
}) | ||
|
||
test(`should navigate to ${CODE_SERVER_ADDRESS}`, options, async ({ page }) => { | ||
test(`should navigate to ${CODE_SERVER_ADDRESS}`, async ({ codeServerPage }) => { | ||
// We navigate codeServer before each test | ||
// and we start the test with a storage state | ||
// which means we should be logged in | ||
// so it should be on the address | ||
const url = page.url() | ||
const url = codeServerPage.page.url() | ||
// We use match because there may be a / at the end | ||
// so we don't want it to fail if we expect http://localhost:8080 to match http://localhost:8080/ | ||
expect(url).toMatch(CODE_SERVER_ADDRESS) | ||
}) | ||
|
||
test("should always see the code-server editor", options, async ({ page }) => { | ||
expect(await codeServer.isEditorVisible()).toBe(true) | ||
test("should always see the code-server editor", async ({ codeServerPage }) => { | ||
expect(await codeServerPage.isEditorVisible()).toBe(true) | ||
}) | ||
|
||
test("should always have a connection", options, async ({ page }) => { | ||
expect(await codeServer.isConnected()).toBe(true) | ||
test("should always have a connection", async ({ codeServerPage }) => { | ||
expect(await codeServerPage.isConnected()).toBe(true) | ||
}) | ||
|
||
test("should show the Integrated Terminal", options, async ({ page }) => { | ||
await codeServer.focusTerminal() | ||
expect(await page.isVisible("#terminal")).toBe(true) | ||
test("should show the Integrated Terminal", async ({ codeServerPage }) => { | ||
await codeServerPage.focusTerminal() | ||
expect(await codeServerPage.page.isVisible("#terminal")).toBe(true) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,15 @@ | ||
import { test, expect } from "@playwright/test" | ||
import { STORAGE } from "../utils/constants" | ||
import { CodeServer } from "./models/CodeServer" | ||
import { storageState } from "../utils/constants" | ||
import { test, expect } from "./baseFixture" | ||
|
||
// This test is to make sure the globalSetup works as expected | ||
// meaning globalSetup ran and stored the storageState in STORAGE | ||
// meaning globalSetup ran and stored the storageState | ||
test.describe("globalSetup", () => { | ||
// Create a new context with the saved storage state | ||
// so we don't have to logged in | ||
const options: any = {} | ||
let codeServer: CodeServer | ||
|
||
// TODO@jsjoeio | ||
// Fix this once https://github.com/microsoft/playwright-test/issues/240 | ||
// is fixed | ||
if (STORAGE) { | ||
const storageState = JSON.parse(STORAGE) || {} | ||
options.contextOptions = { | ||
storageState, | ||
} | ||
} | ||
test.beforeEach(async ({ page }) => { | ||
codeServer = new CodeServer(page) | ||
await codeServer.setup() | ||
test.use({ | ||
storageState, | ||
}) | ||
test("should keep us logged in using the storageState", options, async ({ page }) => { | ||
|
||
test("should keep us logged in using the storageState", async ({ codeServerPage }) => { | ||
// Make sure the editor actually loaded | ||
expect(await codeServer.isEditorVisible()).toBe(true) | ||
expect(await codeServerPage.isEditorVisible()).toBe(true) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,67 @@ | ||
import { test, expect } from "@playwright/test" | ||
import { PASSWORD } from "../utils/constants" | ||
import { CodeServer } from "./models/CodeServer" | ||
import { test, expect } from "./baseFixture" | ||
|
||
test.describe("login", () => { | ||
// Reset the browser so no cookies are persisted | ||
// by emptying the storageState | ||
const options = { | ||
contextOptions: { | ||
storageState: {}, | ||
}, | ||
} | ||
let codeServer: CodeServer | ||
|
||
test.beforeEach(async ({ page }) => { | ||
codeServer = new CodeServer(page) | ||
await codeServer.navigate() | ||
test.use({ | ||
storageState: {}, | ||
}) | ||
|
||
test("should see the login page", options, async ({ page }) => { | ||
test("should see the login page", async ({ codeServerPage }) => { | ||
// It should send us to the login page | ||
expect(await page.title()).toBe("code-server login") | ||
expect(await codeServerPage.page.title()).toBe("code-server login") | ||
}) | ||
|
||
test("should be able to login", options, async ({ page }) => { | ||
test("should be able to login", async ({ codeServerPage }) => { | ||
// Type in password | ||
await page.fill(".password", PASSWORD) | ||
await codeServerPage.page.fill(".password", PASSWORD) | ||
// Click the submit button and login | ||
await page.click(".submit") | ||
await page.waitForLoadState("networkidle") | ||
await codeServerPage.page.click(".submit") | ||
await codeServerPage.page.waitForLoadState("networkidle") | ||
// We do this because occassionally code-server doesn't load on Firefox | ||
// but loads if you reload once or twice | ||
await codeServer.reloadUntilEditorIsReady() | ||
await codeServerPage.reloadUntilEditorIsReady() | ||
// Make sure the editor actually loaded | ||
expect(await codeServer.isEditorVisible()).toBe(true) | ||
expect(await codeServerPage.isEditorVisible()).toBe(true) | ||
}) | ||
|
||
test("should see an error message for missing password", options, async ({ page }) => { | ||
test("should see an error message for missing password", async ({ codeServerPage }) => { | ||
// Skip entering password | ||
// Click the submit button and login | ||
await page.click(".submit") | ||
await page.waitForLoadState("networkidle") | ||
expect(await page.isVisible("text=Missing password")) | ||
await codeServerPage.page.click(".submit") | ||
await codeServerPage.page.waitForLoadState("networkidle") | ||
expect(await codeServerPage.page.isVisible("text=Missing password")) | ||
}) | ||
|
||
test("should see an error message for incorrect password", options, async ({ page }) => { | ||
test("should see an error message for incorrect password", async ({ codeServerPage }) => { | ||
// Type in password | ||
await page.fill(".password", "password123") | ||
await codeServerPage.page.fill(".password", "password123") | ||
// Click the submit button and login | ||
await page.click(".submit") | ||
await page.waitForLoadState("networkidle") | ||
expect(await page.isVisible("text=Incorrect password")) | ||
await codeServerPage.page.click(".submit") | ||
await codeServerPage.page.waitForLoadState("networkidle") | ||
expect(await codeServerPage.page.isVisible("text=Incorrect password")) | ||
}) | ||
|
||
test("should hit the rate limiter for too many unsuccessful logins", options, async ({ page }) => { | ||
test("should hit the rate limiter for too many unsuccessful logins", async ({ codeServerPage }) => { | ||
// Type in password | ||
await page.fill(".password", "password123") | ||
await codeServerPage.page.fill(".password", "password123") | ||
// Click the submit button and login | ||
// The current RateLimiter allows 2 logins per minute plus | ||
// 12 logins per hour for a total of 14 | ||
// See: src/node/routes/login.ts | ||
for (let i = 1; i <= 14; i++) { | ||
await page.click(".submit") | ||
await page.waitForLoadState("networkidle") | ||
await codeServerPage.page.click(".submit") | ||
await codeServerPage.page.waitForLoadState("networkidle") | ||
// We double-check that the correct error message shows | ||
// which should be for incorrect password | ||
expect(await page.isVisible("text=Incorrect password")) | ||
expect(await codeServerPage.page.isVisible("text=Incorrect password")) | ||
} | ||
|
||
// The 15th should fail for a different reason: | ||
// login rate | ||
await page.click(".submit") | ||
await page.waitForLoadState("networkidle") | ||
expect(await page.isVisible("text=Login rate limited!")) | ||
await codeServerPage.page.click(".submit") | ||
await codeServerPage.page.waitForLoadState("networkidle") | ||
expect(await codeServerPage.page.isVisible("text=Login rate limited!")) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the two parameters there, to have all Playwright related configuration in its config file. Also
playwright.config.ts
is easier to understand thanconfig.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed!
playwright.config.ts
is easier to understand. Good call!