This commit is contained in:
Kayla Washburn-Love 2024-05-01 06:45:37 -05:00 committed by GitHub
commit 40406f538b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 1371 additions and 53 deletions

View File

@ -482,6 +482,15 @@ jobs:
# tests are failing.
continue-on-error: true
- name: Upload Chromatic archive
# TODO: We really want this to catch the enterprise snapshots, once those tests
# pass consistently. ie. remove the `!` from `matrix.variant.enterprise` here.
if: ${{ !matrix.variant.enterprise && github.repository_owner == 'coder' }}
uses: actions/upload-artifact@v4
with:
name: playwright-e2e-chromatic-archive
path: ./site/test-results/chromatic-archives/
- name: Upload Playwright Failed Tests
if: always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork
uses: actions/upload-artifact@v4
@ -499,9 +508,8 @@ jobs:
retention-days: 7
chromatic:
# REMARK: this is only used to build storybook and deploy it to Chromatic.
runs-on: ubuntu-latest
needs: changes
needs: [changes, test-e2e]
if: needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true'
steps:
- name: Checkout
@ -514,12 +522,17 @@ jobs:
- name: Setup Node
uses: ./.github/actions/setup-node
- name: Get Playwright results archive
uses: actions/download-artifact@v4
with:
name: playwright-e2e-chromatic-archive
path: ./site/test-results/chromatic-archives/
# This step is not meant for mainline because any detected changes to
# storybook snapshots will require manual approval/review in order for
# the check to pass. This is desired in PRs, but not in mainline.
- name: Publish to Chromatic (non-mainline)
if: github.ref != 'refs/heads/main' && github.repository_owner == 'coder'
uses: chromaui/action@v10
uses: chromaui/action@v11
env:
NODE_OPTIONS: "--max_old_space_size=4096"
STORYBOOK: true
@ -541,6 +554,7 @@ jobs:
onlyChanged: true
# Avoid uploading single files, because that's very slow
zip: true
playwright: true
# This is a separate step for mainline only that auto accepts and changes
# instead of holding CI up. Since we squash/merge, this is defensive to
@ -550,7 +564,7 @@ jobs:
# infinitely "in progress" in mainline unless we re-review each build.
- name: Publish to Chromatic (mainline)
if: github.ref == 'refs/heads/main' && github.repository_owner == 'coder'
uses: chromaui/action@v10
uses: chromaui/action@v11
env:
NODE_OPTIONS: "--max_old_space_size=4096"
STORYBOOK: true
@ -568,6 +582,7 @@ jobs:
onlyChanged: true
# Avoid uploading single files, because that's very slow
zip: true
playwright: true
offlinedocs:
name: offlinedocs

9
site/e2e/testing.ts Normal file
View File

@ -0,0 +1,9 @@
import { test, expect as chromaticExpect } from "@chromatic/test";
import { mergeExpects } from "@playwright/test";
import { expectUrl } from "./expectUrl";
const mergedExpect = mergeExpects(chromaticExpect, expectUrl);
export { mergedExpect as expect };
export { test };
export { chromium, firefox, type Page, webkit } from "@playwright/test";

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import { randomUUID } from "crypto";
import * as http from "http";
import {
@ -9,10 +8,11 @@ import {
stopWorkspace,
} from "../helpers";
import { beforeCoderTest } from "../hooks";
import { test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));
test("app", async ({ context, page }) => {
test.skip("app", async ({ context, page }) => {
const appContent = "Hello World";
const token = randomUUID();
const srv = http

View File

@ -1,10 +1,10 @@
import { expect, test } from "@playwright/test";
import {
createTemplate,
createWorkspace,
requiresEnterpriseLicense,
} from "../helpers";
import { beforeCoderTest } from "../hooks";
import { expect, test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));

View File

@ -1,4 +1,3 @@
import { test, expect } from "@playwright/test";
import {
createTemplate,
createWorkspace,
@ -17,6 +16,7 @@ import {
randParamName,
} from "../parameters";
import type { RichParameter } from "../provisionerGenerated";
import { expect, test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));

View File

@ -1,6 +1,5 @@
import { chromium, expect, test } from "@playwright/test";
import { expectUrl } from "../../expectUrl";
import { randomName, requiresEnterpriseLicense } from "../../helpers";
import { chromium, expect, test } from "../../testing";
test("set application name", async ({ page }) => {
requiresEnterpriseLicense();
@ -75,7 +74,7 @@ test("set service banner", async ({ page }) => {
// Verify service banner
await page.goto("/workspaces", { waitUntil: "domcontentloaded" });
await expectUrl(page).toHavePathName("/workspaces");
await expect(page).toHavePathName("/workspaces");
const bar = page.locator("div.service-banner", { hasText: message });
await expect(bar).toBeVisible();

View File

@ -1,7 +1,7 @@
import { expect, test } from "@playwright/test";
import * as API from "api/api";
import { setupApiCalls } from "../../api";
import { e2eFakeExperiment1, e2eFakeExperiment2 } from "../../constants";
import { expect, test } from "../../testing";
test("experiments", async ({ page }) => {
await setupApiCalls(page);

View File

@ -1,5 +1,5 @@
import { expect, test } from "@playwright/test";
import { requiresEnterpriseLicense } from "../../helpers";
import { expect, test } from "../../testing";
test("license was added successfully", async ({ page }) => {
requiresEnterpriseLicense();

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import { getDeploymentConfig } from "api/api";
import {
setupApiCalls,
@ -8,6 +7,7 @@ import {
verifyConfigFlagNumber,
verifyConfigFlagString,
} from "../../api";
import { test } from "../../testing";
test("enabled network settings", async ({ page }) => {
await setupApiCalls(page);

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import { getDeploymentConfig } from "api/api";
import {
setupApiCalls,
@ -8,6 +7,7 @@ import {
verifyConfigFlagEmpty,
verifyConfigFlagString,
} from "../../api";
import { test } from "../../testing";
test("enabled observability settings", async ({ page }) => {
await setupApiCalls(page);

View File

@ -1,5 +1,3 @@
import type { Page } from "@playwright/test";
import { expect, test } from "@playwright/test";
import type * as API from "api/api";
import { getDeploymentConfig } from "api/api";
import {
@ -9,6 +7,7 @@ import {
verifyConfigFlagNumber,
verifyConfigFlagString,
} from "../../api";
import { expect, type Page, test } from "../../testing";
test("enabled security settings", async ({ page }) => {
await setupApiCalls(page);

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import { getDeploymentConfig } from "api/api";
import {
setupApiCalls,
@ -7,6 +6,7 @@ import {
verifyConfigFlagEntries,
verifyConfigFlagString,
} from "../../api";
import { test } from "../../testing";
test("login with OIDC", async ({ page }) => {
await setupApiCalls(page);

View File

@ -1,9 +1,9 @@
import { test, expect, type Page } from "@playwright/test";
import { createWorkspaceProxy } from "api/api";
import { setupApiCalls } from "../../api";
import { coderPort, workspaceProxyPort } from "../../constants";
import { randomName, requiresEnterpriseLicense } from "../../helpers";
import { startWorkspaceProxy, stopWorkspaceProxy } from "../../proxy";
import { expect, type Page, test } from "../../testing";
test("default proxy is online", async ({ page }) => {
requiresEnterpriseLicense();

View File

@ -1,5 +1,4 @@
import type { Endpoints } from "@octokit/types";
import { test } from "@playwright/test";
import type { ExternalAuthDevice } from "api/typesGenerated";
import { gitAuth } from "../constants";
import {
@ -10,6 +9,7 @@ import {
echoResponsesWithExternalAuth,
} from "../helpers";
import { beforeCoderTest, resetExternalAuthKey } from "../hooks";
import { test } from "../testing";
test.beforeAll(async ({ baseURL }) => {
const srv = await createServer(gitAuth.webPort);

View File

@ -1,4 +1,3 @@
import { test, expect } from "@playwright/test";
import {
createGroup,
createUser,
@ -7,6 +6,7 @@ import {
} from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";
import { expect, test } from "../../testing";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));

View File

@ -1,7 +1,7 @@
import { test, expect } from "@playwright/test";
import { createUser, getCurrentOrgId, setupApiCalls } from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";
import { expect, test } from "../../testing";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));

View File

@ -1,6 +1,6 @@
import { test, expect } from "@playwright/test";
import { randomName, requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";
import { expect, test } from "../../testing";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));

View File

@ -1,7 +1,7 @@
import { test, expect } from "@playwright/test";
import { createGroup, getCurrentOrgId, setupApiCalls } from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";
import { expect, test } from "../../testing";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));

View File

@ -1,7 +1,7 @@
import { test, expect } from "@playwright/test";
import { createGroup, getCurrentOrgId, setupApiCalls } from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";
import { expect, test } from "../../testing";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));

View File

@ -1,4 +1,3 @@
import { test, expect } from "@playwright/test";
import * as API from "api/api";
import {
createGroup,
@ -8,6 +7,7 @@ import {
} from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";
import { expect, test } from "../../testing";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));

View File

@ -1,5 +1,5 @@
import { test, expect } from "@playwright/test";
import { beforeCoderTest } from "../hooks";
import { expect, test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import { randomUUID } from "crypto";
import {
createTemplate,
@ -10,13 +9,14 @@ import {
stopWorkspace,
} from "../helpers";
import { beforeCoderTest } from "../hooks";
import { test } from "../testing";
// we no longer support versions prior to single tailnet: https://github.com/coder/coder/commit/d7cbdbd9c64ad26821e6b35834c59ecf85dcd9d4
const agentVersion = "v0.27.0";
test.beforeEach(({ page }) => beforeCoderTest(page));
test("ssh with agent " + agentVersion, async ({ page }) => {
test.skip("ssh with agent " + agentVersion, async ({ page }) => {
test.setTimeout(40_000); // This is a slow test, 20s may not be enough on Mac.
const token = randomUUID();

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import { randomUUID } from "crypto";
import {
createTemplate,
@ -10,13 +9,14 @@ import {
stopWorkspace,
} from "../helpers";
import { beforeCoderTest } from "../hooks";
import { test } from "../testing";
// we no longer support versions prior to single tailnet: https://github.com/coder/coder/commit/d7cbdbd9c64ad26821e6b35834c59ecf85dcd9d4
const clientVersion = "v0.27.0";
test.beforeEach(({ page }) => beforeCoderTest(page));
test("ssh with client " + clientVersion, async ({ page }) => {
test.skip("ssh with client " + clientVersion, async ({ page }) => {
const token = randomUUID();
const template = await createTemplate(page, {
apply: [

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import {
buildWorkspaceWithParameters,
createTemplate,
@ -9,6 +8,7 @@ import {
import { beforeCoderTest } from "../hooks";
import { firstBuildOption, secondBuildOption } from "../parameters";
import type { RichParameter } from "../provisionerGenerated";
import { test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import {
buildWorkspaceWithParameters,
createTemplate,
@ -10,10 +9,11 @@ import {
import { beforeCoderTest } from "../hooks";
import { firstBuildOption, secondBuildOption } from "../parameters";
import type { RichParameter } from "../provisionerGenerated";
import { test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));
test("start workspace with ephemeral parameters", async ({ page }) => {
test.skip("start workspace with ephemeral parameters", async ({ page }) => {
const richParameters: RichParameter[] = [firstBuildOption, secondBuildOption];
const template = await createTemplate(
page,

View File

@ -1,5 +1,3 @@
import { expect, test } from "@playwright/test";
import { expectUrl } from "../expectUrl";
import {
createGroup,
createTemplate,
@ -7,6 +5,7 @@ import {
updateTemplateSettings,
} from "../helpers";
import { beforeCoderTest } from "../hooks";
import { expect, test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));
@ -29,7 +28,7 @@ test("add and remove a group", async ({ page }) => {
await page.goto(`/templates/${templateName}/settings/permissions`, {
waitUntil: "domcontentloaded",
});
await expectUrl(page).toHavePathName(
await expect(page).toHavePathName(
`/templates/${templateName}/settings/permissions`,
);
@ -59,7 +58,7 @@ test("require latest version", async ({ page }) => {
await page.goto(`/templates/${templateName}/settings`, {
waitUntil: "domcontentloaded",
});
await expectUrl(page).toHavePathName(`/templates/${templateName}/settings`);
await expect(page).toHavePathName(`/templates/${templateName}/settings`);
let checkbox = await page.waitForSelector("#require_active_version");
await checkbox.click();
await page.getByTestId("form-submit").click();

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import {
createTemplate,
createWorkspace,
@ -17,6 +16,7 @@ import {
secondBuildOption,
} from "../parameters";
import type { RichParameter } from "../provisionerGenerated";
import { test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));

View File

@ -1,8 +1,8 @@
import { test, expect } from "@playwright/test";
import { randomName } from "../../helpers";
import { beforeCoderTest } from "../../hooks";
import { expect, test } from "../../testing";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
test.beforeEach(({ page }) => beforeCoderTest(page));
test("create user with password", async ({ page, baseURL }) => {
await page.goto(`${baseURL}/users`, { waitUntil: "domcontentloaded" });

View File

@ -1,6 +1,6 @@
import { test, expect } from "@playwright/test";
import { createUser, getCurrentOrgId, setupApiCalls } from "../../api";
import { beforeCoderTest } from "../../hooks";
import { expect, test } from "../../testing";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));

View File

@ -1,4 +1,3 @@
import { test } from "@playwright/test";
import { randomUUID } from "crypto";
import {
createTemplate,
@ -8,10 +7,11 @@ import {
stopAgent,
} from "../helpers";
import { beforeCoderTest } from "../hooks";
import { test } from "../testing";
test.beforeEach(({ page }) => beforeCoderTest(page));
test("web terminal", async ({ context, page }) => {
test.skip("web terminal", async ({ context, page }) => {
const token = randomUUID();
const template = await createTemplate(page, {
apply: [

View File

@ -95,6 +95,7 @@
"yup": "1.3.2"
},
"devDependencies": {
"@chromatic/test": "npm:@chromatic-com/playwright@0.6.6",
"@octokit/types": "12.3.0",
"@playwright/test": "1.40.1",
"@storybook/addon-actions": "8.0.5",
@ -158,6 +159,7 @@
"jest-websocket-mock": "2.5.0",
"jest_workaround": "0.1.14",
"msw": "2.2.3",
"playwright": "1.40.1",
"prettier": "3.1.0",
"protobufjs": "7.2.4",
"rxjs": "7.8.1",

File diff suppressed because it is too large Load Diff