mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: turn e2e enterprise tests into e2e premium tests (#14979)
This commit is contained in:
+12
-15
@@ -549,8 +549,8 @@ jobs:
|
||||
working-directory: site
|
||||
|
||||
test-e2e:
|
||||
runs-on: ${{ github.repository_owner == 'coder' && (matrix.variant.enterprise && 'depot-ubuntu-22.04' || 'depot-ubuntu-22.04-4') || 'ubuntu-latest' }}
|
||||
# test-e2e fails on 2-core 8GB runners, so we use the 4-core 16GB runner
|
||||
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }}
|
||||
needs: changes
|
||||
if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
|
||||
timeout-minutes: 20
|
||||
@@ -558,10 +558,10 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant:
|
||||
- enterprise: false
|
||||
- premium: false
|
||||
name: test-e2e
|
||||
- enterprise: true
|
||||
name: test-e2e-enterprise
|
||||
- premium: true
|
||||
name: test-e2e-premium
|
||||
name: ${{ matrix.variant.name }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
@@ -590,30 +590,27 @@ jobs:
|
||||
- run: pnpm playwright:install
|
||||
working-directory: site
|
||||
|
||||
# Run tests that don't require an enterprise license without an enterprise license
|
||||
# Run tests that don't require a premium license without a premium license
|
||||
- run: pnpm playwright:test --forbid-only --workers 1
|
||||
if: ${{ !matrix.variant.enterprise }}
|
||||
if: ${{ !matrix.variant.premium }}
|
||||
env:
|
||||
DEBUG: pw:api
|
||||
working-directory: site
|
||||
|
||||
# Run all of the tests with an enterprise license
|
||||
# Run all of the tests with a premium license
|
||||
- run: pnpm playwright:test --forbid-only --workers 1
|
||||
if: ${{ matrix.variant.enterprise }}
|
||||
if: ${{ matrix.variant.premium }}
|
||||
env:
|
||||
DEBUG: pw:api
|
||||
CODER_E2E_ENTERPRISE_LICENSE: ${{ secrets.CODER_E2E_ENTERPRISE_LICENSE }}
|
||||
CODER_E2E_REQUIRE_ENTERPRISE_TESTS: "1"
|
||||
CODER_E2E_LICENSE: ${{ secrets.CODER_E2E_LICENSE }}
|
||||
CODER_E2E_REQUIRE_PREMIUM_TESTS: "1"
|
||||
working-directory: site
|
||||
# Temporarily allow these to fail so that I can gather data about which
|
||||
# tests are failing.
|
||||
continue-on-error: true
|
||||
|
||||
- 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@604373da6381bf24206979c74d06a550515601b9 # v4.4.1
|
||||
with:
|
||||
name: failed-test-videos${{ matrix.variant.enterprise && '-enterprise' || '-agpl' }}
|
||||
name: failed-test-videos${{ matrix.variant.premium && '-premium' || '' }}
|
||||
path: ./site/test-results/**/*.webm
|
||||
retention-days: 7
|
||||
|
||||
@@ -621,7 +618,7 @@ jobs:
|
||||
if: always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork
|
||||
uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1
|
||||
with:
|
||||
name: debug-pprof-dumps${{ matrix.variant.enterprise && '-enterprise' || '-agpl' }}
|
||||
name: debug-pprof-dumps${{ matrix.variant.premium && '-premium' || '' }}
|
||||
path: ./site/test-results/**/debug-pprof-*.txt
|
||||
retention-days: 7
|
||||
|
||||
|
||||
+18
-3
@@ -13,6 +13,9 @@ export const workspaceProxyPort = 3112;
|
||||
export const agentPProfPort = 6061;
|
||||
export const coderdPProfPort = 6062;
|
||||
|
||||
// The name of the organization that should be used by default when needed.
|
||||
export const defaultOrganizationName = "coder";
|
||||
|
||||
// Credentials for the first user
|
||||
export const username = "admin";
|
||||
export const password = "SomeSecurePassword!";
|
||||
@@ -34,10 +37,22 @@ export const gitAuth = {
|
||||
installationsPath: "/installations",
|
||||
};
|
||||
|
||||
export const requireEnterpriseTests = Boolean(
|
||||
process.env.CODER_E2E_REQUIRE_ENTERPRISE_TESTS,
|
||||
/**
|
||||
* Will make the tests fail if set to `true` and a license was not provided.
|
||||
*/
|
||||
export const premiumTestsRequired = Boolean(
|
||||
process.env.CODER_E2E_REQUIRE_PREMIUM_TESTS,
|
||||
);
|
||||
export const enterpriseLicense = process.env.CODER_E2E_ENTERPRISE_LICENSE ?? "";
|
||||
|
||||
export const license = process.env.CODER_E2E_LICENSE ?? "";
|
||||
|
||||
/**
|
||||
* Certain parts of the UI change when organizations are enabled. Organizations
|
||||
* are enabled by a license entitlement, and license configuration is guaranteed
|
||||
* to run before any other tests, so having this as a bit of "global state" is
|
||||
* fine.
|
||||
*/
|
||||
export const organizationsEnabled = Boolean(license);
|
||||
|
||||
// Disabling terraform tests is optional for environments without Docker + Terraform.
|
||||
// By default, we opt into these tests.
|
||||
|
||||
+41
-1
@@ -4,7 +4,8 @@ type PollingOptions = { timeout?: number; intervals?: number[] };
|
||||
|
||||
export const expectUrl = expect.extend({
|
||||
/**
|
||||
* toHavePathName is an alternative to `toHaveURL` that won't fail if the URL contains query parameters.
|
||||
* toHavePathName is an alternative to `toHaveURL` that won't fail if the URL
|
||||
* contains query parameters.
|
||||
*/
|
||||
async toHavePathName(page: Page, expected: string, options?: PollingOptions) {
|
||||
let actual: string = new URL(page.url()).pathname;
|
||||
@@ -34,4 +35,43 @@ export const expectUrl = expect.extend({
|
||||
)}\nActual: ${this.utils.printReceived(actual)}`,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* toHavePathNameEndingWith allows checking the end of the URL (ie. to make
|
||||
* sure we redirected to a specific page) without caring about the entire URL,
|
||||
* which might depend on things like whether or not organizations or other
|
||||
* features are enabled.
|
||||
*/
|
||||
async toHavePathNameEndingWith(
|
||||
page: Page,
|
||||
expected: string,
|
||||
options?: PollingOptions,
|
||||
) {
|
||||
let actual: string = new URL(page.url()).pathname;
|
||||
let pass: boolean;
|
||||
try {
|
||||
await expect
|
||||
.poll(() => {
|
||||
actual = new URL(page.url()).pathname;
|
||||
return actual.endsWith(expected);
|
||||
}, options)
|
||||
.toBe(true);
|
||||
pass = true;
|
||||
} catch {
|
||||
pass = false;
|
||||
}
|
||||
|
||||
return {
|
||||
name: "toHavePathNameEndingWith",
|
||||
pass,
|
||||
actual,
|
||||
expected,
|
||||
message: () =>
|
||||
`The page does not have the expected URL pathname.\nExpected a url ${
|
||||
this.isNot ? "not " : ""
|
||||
}ending with: ${this.utils.printExpected(
|
||||
expected,
|
||||
)}\nActual: ${this.utils.printReceived(actual)}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -28,16 +28,16 @@ test("setup deployment", async ({ page }) => {
|
||||
await page.getByTestId("button-select-template").isVisible();
|
||||
|
||||
// Setup license
|
||||
if (constants.requireEnterpriseTests || constants.enterpriseLicense) {
|
||||
if (constants.premiumTestsRequired || constants.license) {
|
||||
// Make sure that we have something that looks like a real license
|
||||
expect(constants.enterpriseLicense).toBeTruthy();
|
||||
expect(constants.enterpriseLicense.length).toBeGreaterThan(92); // the signature alone should be this long
|
||||
expect(constants.enterpriseLicense.split(".").length).toBe(3); // otherwise it's invalid
|
||||
expect(constants.license).toBeTruthy();
|
||||
expect(constants.license.length).toBeGreaterThan(92); // the signature alone should be this long
|
||||
expect(constants.license.split(".").length).toBe(3); // otherwise it's invalid
|
||||
|
||||
await page.goto("/deployment/licenses", { waitUntil: "domcontentloaded" });
|
||||
|
||||
await page.getByText("Add a license").click();
|
||||
await page.getByRole("textbox").fill(constants.enterpriseLicense);
|
||||
await page.getByRole("textbox").fill(constants.license);
|
||||
await page.getByText("Upload License").click();
|
||||
|
||||
await expect(
|
||||
|
||||
+68
-33
@@ -16,9 +16,10 @@ import {
|
||||
agentPProfPort,
|
||||
coderMain,
|
||||
coderPort,
|
||||
enterpriseLicense,
|
||||
defaultOrganizationName,
|
||||
license,
|
||||
premiumTestsRequired,
|
||||
prometheusPort,
|
||||
requireEnterpriseTests,
|
||||
requireTerraformTests,
|
||||
} from "./constants";
|
||||
import { expectUrl } from "./expectUrl";
|
||||
@@ -35,22 +36,28 @@ import {
|
||||
type RichParameter,
|
||||
} from "./provisionerGenerated";
|
||||
|
||||
// requiresEnterpriseLicense will skip the test if we're not running with an enterprise license
|
||||
export function requiresEnterpriseLicense() {
|
||||
if (requireEnterpriseTests) {
|
||||
/**
|
||||
* requiresLicense will skip the test if we're not running with a license added
|
||||
*/
|
||||
export function requiresLicense() {
|
||||
if (premiumTestsRequired) {
|
||||
return;
|
||||
}
|
||||
|
||||
test.skip(!enterpriseLicense);
|
||||
test.skip(!license);
|
||||
}
|
||||
|
||||
// requireTerraformProvisioner by default is enabled.
|
||||
/**
|
||||
* requireTerraformProvisioner by default is enabled.
|
||||
*/
|
||||
export function requireTerraformProvisioner() {
|
||||
test.skip(!requireTerraformTests);
|
||||
}
|
||||
|
||||
// createWorkspace creates a workspace for a template.
|
||||
// It does not wait for it to be running, but it does navigate to the page.
|
||||
/**
|
||||
* createWorkspace creates a workspace for a template. It does not wait for it
|
||||
* to be running, but it does navigate to the page.
|
||||
*/
|
||||
export const createWorkspace = async (
|
||||
page: Page,
|
||||
templateName: string,
|
||||
@@ -90,7 +97,7 @@ export const createWorkspace = async (
|
||||
|
||||
await expectUrl(page).toHavePathName(`/@admin/${name}`);
|
||||
|
||||
await page.waitForSelector("*[data-testid='build-status'] >> text=Running", {
|
||||
await page.waitForSelector("[data-testid='build-status'] >> text=Running", {
|
||||
state: "visible",
|
||||
});
|
||||
return name;
|
||||
@@ -151,8 +158,10 @@ export const verifyParameters = async (
|
||||
}
|
||||
};
|
||||
|
||||
// StarterTemplates are ids of starter templates that can be used in place of
|
||||
// the responses payload. These starter templates will require real provisioners.
|
||||
/**
|
||||
* StarterTemplates are ids of starter templates that can be used in place of
|
||||
* the responses payload. These starter templates will require real provisioners.
|
||||
*/
|
||||
export enum StarterTemplates {
|
||||
STARTER_DOCKER = "docker",
|
||||
}
|
||||
@@ -166,11 +175,14 @@ function isStarterTemplate(
|
||||
return typeof input === "string";
|
||||
}
|
||||
|
||||
// createTemplate navigates to the /templates/new page and uploads a template
|
||||
// with the resources provided in the responses argument.
|
||||
/**
|
||||
* createTemplate navigates to the /templates/new page and uploads a template
|
||||
* with the resources provided in the responses argument.
|
||||
*/
|
||||
export const createTemplate = async (
|
||||
page: Page,
|
||||
responses?: EchoProvisionerResponses | StarterTemplates,
|
||||
orgName = defaultOrganizationName,
|
||||
): Promise<string> => {
|
||||
let path = "/templates/new";
|
||||
if (isStarterTemplate(responses)) {
|
||||
@@ -191,17 +203,33 @@ export const createTemplate = async (
|
||||
});
|
||||
}
|
||||
|
||||
// If the organization picker is present on the page, select the default
|
||||
// organization.
|
||||
const orgPicker = page.getByLabel("Belongs to *");
|
||||
const organizationsEnabled = await orgPicker.isVisible();
|
||||
if (organizationsEnabled) {
|
||||
await orgPicker.click();
|
||||
await page.getByText(orgName, { exact: true }).click();
|
||||
}
|
||||
|
||||
const name = randomName();
|
||||
await page.getByLabel("Name *").fill(name);
|
||||
await page.getByTestId("form-submit").click();
|
||||
await expectUrl(page).toHavePathName(`/templates/${name}/files`, {
|
||||
timeout: 30000,
|
||||
});
|
||||
await expectUrl(page).toHavePathName(
|
||||
organizationsEnabled
|
||||
? `/templates/${orgName}/${name}/files`
|
||||
: `/templates/${name}/files`,
|
||||
{
|
||||
timeout: 30000,
|
||||
},
|
||||
);
|
||||
return name;
|
||||
};
|
||||
|
||||
// createGroup navigates to the /groups/create page and creates a group with a
|
||||
// random name.
|
||||
/**
|
||||
* createGroup navigates to the /groups/create page and creates a group with a
|
||||
* random name.
|
||||
*/
|
||||
export const createGroup = async (page: Page): Promise<string> => {
|
||||
await page.goto("/groups/create", { waitUntil: "domcontentloaded" });
|
||||
await expectUrl(page).toHavePathName("/groups/create");
|
||||
@@ -209,13 +237,13 @@ export const createGroup = async (page: Page): Promise<string> => {
|
||||
const name = randomName();
|
||||
await page.getByLabel("Name", { exact: true }).fill(name);
|
||||
await page.getByTestId("form-submit").click();
|
||||
await expect(page).toHaveURL(
|
||||
/\/groups\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,
|
||||
);
|
||||
await expectUrl(page).toHavePathName(`/groups/${name}`);
|
||||
return name;
|
||||
};
|
||||
|
||||
// sshIntoWorkspace spawns a Coder SSH process and a client connected to it.
|
||||
/**
|
||||
* sshIntoWorkspace spawns a Coder SSH process and a client connected to it.
|
||||
*/
|
||||
export const sshIntoWorkspace = async (
|
||||
page: Page,
|
||||
workspace: string,
|
||||
@@ -298,8 +326,10 @@ export const buildWorkspaceWithParameters = async (
|
||||
});
|
||||
};
|
||||
|
||||
// startAgent runs the coder agent with the provided token.
|
||||
// It awaits the agent to be ready before returning.
|
||||
/**
|
||||
* startAgent runs the coder agent with the provided token. It waits for the
|
||||
* agent to be ready before returning.
|
||||
*/
|
||||
export const startAgent = async (
|
||||
page: Page,
|
||||
token: string,
|
||||
@@ -307,8 +337,10 @@ export const startAgent = async (
|
||||
return startAgentWithCommand(page, token, "go", "run", coderMain);
|
||||
};
|
||||
|
||||
// downloadCoderVersion downloads the version provided into a temporary dir and
|
||||
// caches it so subsequent calls are fast.
|
||||
/**
|
||||
* downloadCoderVersion downloads the version provided into a temporary dir and
|
||||
* caches it so subsequent calls are fast.
|
||||
*/
|
||||
export const downloadCoderVersion = async (
|
||||
version: string,
|
||||
): Promise<string> => {
|
||||
@@ -448,8 +480,10 @@ interface EchoProvisionerResponses {
|
||||
apply?: RecursivePartial<Response>[];
|
||||
}
|
||||
|
||||
// createTemplateVersionTar consumes a series of echo provisioner protobufs and
|
||||
// converts it into an uploadable tar file.
|
||||
/**
|
||||
* createTemplateVersionTar consumes a series of echo provisioner protobufs and
|
||||
* converts it into an uploadable tar file.
|
||||
*/
|
||||
const createTemplateVersionTar = async (
|
||||
responses?: EchoProvisionerResponses,
|
||||
): Promise<Buffer> => {
|
||||
@@ -619,8 +653,10 @@ export const randomName = () => {
|
||||
return randomUUID().slice(0, 8);
|
||||
};
|
||||
|
||||
// Awaiter is a helper that allows you to wait for a callback to be called.
|
||||
// It is useful for waiting for events to occur.
|
||||
/**
|
||||
* Awaiter is a helper that allows you to wait for a callback to be called. It
|
||||
* is useful for waiting for events to occur.
|
||||
*/
|
||||
export class Awaiter {
|
||||
private promise: Promise<void>;
|
||||
private callback?: () => void;
|
||||
@@ -825,7 +861,6 @@ export const updateTemplateSettings = async (
|
||||
await page.goto(`/templates/${templateName}/settings`, {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
await expectUrl(page).toHavePathName(`/templates/${templateName}/settings`);
|
||||
|
||||
for (const [key, value] of Object.entries(templateSettingValues)) {
|
||||
// Skip max_port_share_level for now since the frontend is not yet able to handle it
|
||||
@@ -839,7 +874,7 @@ export const updateTemplateSettings = async (
|
||||
await page.getByTestId("form-submit").click();
|
||||
|
||||
const name = templateSettingValues.name ?? templateName;
|
||||
await expectUrl(page).toHavePathName(`/templates/${name}`);
|
||||
await expectUrl(page).toHavePathNameEndingWith(`/${name}`);
|
||||
};
|
||||
|
||||
export const updateWorkspace = async (
|
||||
|
||||
@@ -70,6 +70,11 @@ export default defineConfig({
|
||||
],
|
||||
reporter: [["./reporter.ts"]],
|
||||
use: {
|
||||
// It'd be very nice to add this, but there are some tests that need
|
||||
// tweaking to make it work consistently (notably, ones that wait for agent
|
||||
// stats on the workspace page. The default is like 50 seconds, which is
|
||||
// way too long and makes it painful to wait for test runs in CI.
|
||||
// actionTimeout: 5000, // 5 seconds
|
||||
baseURL: `http://localhost:${coderPort}`,
|
||||
video: "retain-on-failure",
|
||||
...(wsEndpoint
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
TestResult,
|
||||
} from "@playwright/test/reporter";
|
||||
import { API } from "api/api";
|
||||
import { coderdPProfPort, enterpriseLicense } from "./constants";
|
||||
import { coderdPProfPort, license } from "./constants";
|
||||
|
||||
class CoderReporter implements Reporter {
|
||||
config: FullConfig | null = null;
|
||||
@@ -108,9 +108,9 @@ class CoderReporter implements Reporter {
|
||||
|
||||
onEnd(result: FullResult) {
|
||||
console.info(`==> Tests ${result.status}`);
|
||||
if (!enterpriseLicense) {
|
||||
if (!license) {
|
||||
console.info(
|
||||
"==> Enterprise tests were skipped, because no license was provided",
|
||||
"==> Tests that require a license were skipped, because no license was provided",
|
||||
);
|
||||
}
|
||||
console.info(`${this.passedCount} passed`);
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import {
|
||||
createTemplate,
|
||||
createWorkspace,
|
||||
requiresEnterpriseLicense,
|
||||
} from "../helpers";
|
||||
import { createTemplate, createWorkspace, requiresLicense } from "../helpers";
|
||||
import { beforeCoderTest } from "../hooks";
|
||||
|
||||
test.beforeEach(({ page }) => beforeCoderTest(page));
|
||||
|
||||
test("inspecting and filtering audit logs", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
|
||||
const userName = "admin";
|
||||
// Do some stuff that should show up in the audit logs
|
||||
@@ -48,20 +44,24 @@ test("inspecting and filtering audit logs", async ({ page }) => {
|
||||
|
||||
// Filter by resource type
|
||||
await page.getByText("All resource types").click();
|
||||
await page.getByRole("menu").getByText("Workspace Build").click();
|
||||
const workspaceBuildsOption = page.getByText("Workspace Build");
|
||||
await workspaceBuildsOption.scrollIntoViewIfNeeded({ timeout: 5000 });
|
||||
await workspaceBuildsOption.click();
|
||||
// Our workspace build should be visible
|
||||
await expect(page.getByText(startedWorkspaceMessage)).toBeVisible();
|
||||
// Logins should no longer be visible
|
||||
await expect(page.getByText(loginMessage)).not.toBeVisible();
|
||||
|
||||
// Clear filters, everything should be visible again
|
||||
await page.getByLabel("Clear filter").click();
|
||||
await page.getByLabel("Clear search").click();
|
||||
await expect(page.getByText(startedWorkspaceMessage)).toBeVisible();
|
||||
await expect(page.getByText(loginMessage)).toBeVisible();
|
||||
|
||||
// Filter by action type
|
||||
await page.getByText("All actions").click();
|
||||
await page.getByRole("menu").getByText("Login").click();
|
||||
const loginOption = page.getByText("Login");
|
||||
await loginOption.scrollIntoViewIfNeeded({ timeout: 5000 });
|
||||
await loginOption.click();
|
||||
// Logins should be visible
|
||||
await expect(page.getByText(loginMessage)).toBeVisible();
|
||||
// Our workspace build should no longer be visible
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { chromium, expect, test } from "@playwright/test";
|
||||
import { expectUrl } from "../../expectUrl";
|
||||
import { randomName, requiresEnterpriseLicense } from "../../helpers";
|
||||
import { randomName, requiresLicense } from "../../helpers";
|
||||
|
||||
test("set application name", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
|
||||
await page.goto("/deployment/appearance", { waitUntil: "domcontentloaded" });
|
||||
|
||||
@@ -33,7 +33,7 @@ test("set application name", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("set application logo", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
|
||||
await page.goto("/deployment/appearance", { waitUntil: "domcontentloaded" });
|
||||
|
||||
@@ -61,17 +61,17 @@ test("set application logo", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("set service banner", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
|
||||
await page.goto("/deployment/appearance", { waitUntil: "domcontentloaded" });
|
||||
|
||||
const message = "Mary has a little lamb.";
|
||||
|
||||
// Fill out the form
|
||||
const form = page.locator("form", { hasText: "Service Banner" });
|
||||
await form.getByLabel("Enabled", { exact: true }).check();
|
||||
await page.getByRole("button", { name: "New" }).click();
|
||||
const form = page.getByRole("presentation");
|
||||
await form.getByLabel("Message", { exact: true }).fill(message);
|
||||
await form.getByRole("button", { name: "Submit" }).click();
|
||||
await form.getByRole("button", { name: "Update" }).click();
|
||||
|
||||
// Verify service banner
|
||||
await page.goto("/workspaces", { waitUntil: "domcontentloaded" });
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { requiresEnterpriseLicense } from "../../helpers";
|
||||
import { requiresLicense } from "../../helpers";
|
||||
|
||||
test("license was added successfully", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
|
||||
await page.goto("/deployment/licenses", { waitUntil: "domcontentloaded" });
|
||||
const firstLicense = page.locator(".licenses > .license-card", {
|
||||
@@ -12,11 +12,7 @@ test("license was added successfully", async ({ page }) => {
|
||||
|
||||
// Trial vs. Enterprise?
|
||||
const accountType = firstLicense.locator(".account-type");
|
||||
await expect(accountType).toHaveText("Enterprise");
|
||||
|
||||
// User limit 1/1
|
||||
const userLimit = firstLicense.locator(".user-limit");
|
||||
await expect(userLimit).toHaveText("1 / 1");
|
||||
await expect(accountType).toHaveText("Premium");
|
||||
|
||||
// License should not be expired yet
|
||||
const licenseExpires = firstLicense.locator(".license-expires");
|
||||
|
||||
@@ -2,11 +2,11 @@ import { type Page, expect, test } from "@playwright/test";
|
||||
import { API } from "api/api";
|
||||
import { setupApiCalls } from "../../api";
|
||||
import { coderPort, workspaceProxyPort } from "../../constants";
|
||||
import { randomName, requiresEnterpriseLicense } from "../../helpers";
|
||||
import { randomName, requiresLicense } from "../../helpers";
|
||||
import { startWorkspaceProxy, stopWorkspaceProxy } from "../../proxy";
|
||||
|
||||
test("default proxy is online", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
await setupApiCalls(page);
|
||||
|
||||
await page.goto("/deployment/workspace-proxies", {
|
||||
@@ -28,7 +28,7 @@ test("default proxy is online", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("custom proxy is online", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
await setupApiCalls(page);
|
||||
|
||||
const proxyName = randomName();
|
||||
|
||||
@@ -5,13 +5,13 @@ import {
|
||||
getCurrentOrgId,
|
||||
setupApiCalls,
|
||||
} from "../../api";
|
||||
import { requiresEnterpriseLicense } from "../../helpers";
|
||||
import { requiresLicense } from "../../helpers";
|
||||
import { beforeCoderTest } from "../../hooks";
|
||||
|
||||
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
|
||||
|
||||
test("add members", async ({ page, baseURL }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
await setupApiCalls(page);
|
||||
const orgId = await getCurrentOrgId();
|
||||
const group = await createGroup(orgId);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { createUser, getCurrentOrgId, setupApiCalls } from "../../api";
|
||||
import { requiresEnterpriseLicense } from "../../helpers";
|
||||
import { requiresLicense } from "../../helpers";
|
||||
import { beforeCoderTest } from "../../hooks";
|
||||
|
||||
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
|
||||
@@ -11,7 +11,7 @@ test(`Every user should be automatically added to the default '${DEFAULT_GROUP_N
|
||||
page,
|
||||
baseURL,
|
||||
}) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
await setupApiCalls(page);
|
||||
const orgId = await getCurrentOrgId();
|
||||
const numberOfMembers = 3;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { randomName, requiresEnterpriseLicense } from "../../helpers";
|
||||
import { randomName, requiresLicense } from "../../helpers";
|
||||
import { beforeCoderTest } from "../../hooks";
|
||||
|
||||
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
|
||||
|
||||
test("create group", async ({ page, baseURL }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
await page.goto(`${baseURL}/groups`, { waitUntil: "domcontentloaded" });
|
||||
await expect(page).toHaveTitle("Groups - Coder");
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { createGroup, getCurrentOrgId, setupApiCalls } from "../../api";
|
||||
import { requiresEnterpriseLicense } from "../../helpers";
|
||||
import { beforeCoderTest } from "../../hooks";
|
||||
|
||||
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
|
||||
|
||||
test("navigate to group page", async ({ page, baseURL }) => {
|
||||
requiresEnterpriseLicense();
|
||||
await setupApiCalls(page);
|
||||
const orgId = await getCurrentOrgId();
|
||||
const group = await createGroup(orgId);
|
||||
|
||||
await page.goto(`${baseURL}/users`, { waitUntil: "domcontentloaded" });
|
||||
await expect(page).toHaveTitle("Users - Coder");
|
||||
|
||||
await page.getByRole("link", { name: "Groups" }).click();
|
||||
await expect(page).toHaveTitle("Groups - Coder");
|
||||
|
||||
const groupRow = page.getByRole("row", { name: group.display_name });
|
||||
await groupRow.click();
|
||||
await expect(page).toHaveTitle(`${group.display_name} - Coder`);
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { createGroup, getCurrentOrgId, setupApiCalls } from "../../api";
|
||||
import { requiresEnterpriseLicense } from "../../helpers";
|
||||
import { requiresLicense } from "../../helpers";
|
||||
import { beforeCoderTest } from "../../hooks";
|
||||
|
||||
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
|
||||
|
||||
test("remove group", async ({ page, baseURL }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
await setupApiCalls(page);
|
||||
const orgId = await getCurrentOrgId();
|
||||
const group = await createGroup(orgId);
|
||||
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
getCurrentOrgId,
|
||||
setupApiCalls,
|
||||
} from "../../api";
|
||||
import { requiresEnterpriseLicense } from "../../helpers";
|
||||
import { requiresLicense } from "../../helpers";
|
||||
import { beforeCoderTest } from "../../hooks";
|
||||
|
||||
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
|
||||
|
||||
test("remove member", async ({ page, baseURL }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
await setupApiCalls(page);
|
||||
const orgId = await getCurrentOrgId();
|
||||
const [group, member] = await Promise.all([
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { setupApiCalls } from "../api";
|
||||
import { expectUrl } from "../expectUrl";
|
||||
import { requiresEnterpriseLicense } from "../helpers";
|
||||
import { requiresLicense } from "../helpers";
|
||||
import { beforeCoderTest } from "../hooks";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
@@ -9,15 +9,15 @@ test.beforeEach(async ({ page }) => {
|
||||
await setupApiCalls(page);
|
||||
});
|
||||
|
||||
test("create and delete organization", async ({ page, baseURL }) => {
|
||||
requiresEnterpriseLicense();
|
||||
test("create and delete organization", async ({ page }) => {
|
||||
requiresLicense();
|
||||
|
||||
// Create an organization
|
||||
await page.goto(`${baseURL}/organizations/new`, {
|
||||
await page.goto("/organizations/new", {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
|
||||
await page.getByLabel("Name", { exact: true }).fill("floop");
|
||||
await page.getByLabel("Slug", { exact: true }).fill("floop");
|
||||
await page.getByLabel("Display name").fill("Floop");
|
||||
await page.getByLabel("Description").fill("Org description floop");
|
||||
await page.getByLabel("Icon", { exact: true }).fill("/emojis/1f957.png");
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { defaultOrganizationName } from "../constants";
|
||||
import { expectUrl } from "../expectUrl";
|
||||
import {
|
||||
createGroup,
|
||||
createTemplate,
|
||||
requiresEnterpriseLicense,
|
||||
requiresLicense,
|
||||
updateTemplateSettings,
|
||||
} from "../helpers";
|
||||
import { beforeCoderTest } from "../hooks";
|
||||
@@ -14,23 +15,21 @@ test("template update with new name redirects on successful submit", async ({
|
||||
page,
|
||||
}) => {
|
||||
const templateName = await createTemplate(page);
|
||||
|
||||
await updateTemplateSettings(page, templateName, {
|
||||
name: "new-name",
|
||||
});
|
||||
});
|
||||
|
||||
test("add and remove a group", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
|
||||
const templateName = await createTemplate(page);
|
||||
const orgName = defaultOrganizationName;
|
||||
const templateName = await createTemplate(page, undefined, orgName);
|
||||
const groupName = await createGroup(page);
|
||||
|
||||
await page.goto(`/templates/${templateName}/settings/permissions`, {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
await expectUrl(page).toHavePathName(
|
||||
`/templates/${templateName}/settings/permissions`,
|
||||
await page.goto(
|
||||
`/templates/${orgName}/${templateName}/settings/permissions`,
|
||||
{ waitUntil: "domcontentloaded" },
|
||||
);
|
||||
|
||||
// Type the first half of the group name
|
||||
@@ -52,7 +51,7 @@ test("add and remove a group", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("require latest version", async ({ page }) => {
|
||||
requiresEnterpriseLicense();
|
||||
requiresLicense();
|
||||
|
||||
const templateName = await createTemplate(page);
|
||||
|
||||
|
||||
@@ -40,6 +40,6 @@ export const ClearValue: Story = {
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(canvas.getByRole("button", { name: "Clear field" }));
|
||||
await userEvent.click(canvas.getByRole("button", { name: "Clear search" }));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ export const SearchField: FC<SearchFieldProps> = ({
|
||||
),
|
||||
endAdornment: value !== "" && (
|
||||
<InputAdornment position="end">
|
||||
<Tooltip title="Clear field">
|
||||
<Tooltip title="Clear search">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => {
|
||||
@@ -48,7 +48,7 @@ export const SearchField: FC<SearchFieldProps> = ({
|
||||
}}
|
||||
>
|
||||
<CloseIcon css={{ fontSize: 14 }} />
|
||||
<span css={{ ...visuallyHidden }}>Clear field</span>
|
||||
<span css={{ ...visuallyHidden }}>Clear search</span>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</InputAdornment>
|
||||
|
||||
Reference in New Issue
Block a user