test(site): e2e: create workspace with rich parameters (#9185)

This commit is contained in:
Marcin Tojek 2023-08-22 14:21:32 +02:00 committed by GitHub
parent 545a256b57
commit 8a1da743cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 374 additions and 2 deletions

View File

@ -13,29 +13,135 @@ import {
Provision_Complete,
Provision_Response,
Resource,
RichParameter,
} from "./provisionerGenerated"
import { port } from "./playwright.config"
import * as ssh from "ssh2"
import { Duplex } from "stream"
import { WorkspaceBuildParameter } from "api/typesGenerated"
// 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,
richParameters: RichParameter[] = [],
buildParameters: WorkspaceBuildParameter[] = [],
): Promise<string> => {
await page.goto("/templates/" + templateName + "/workspace", {
waitUntil: "networkidle",
})
const name = randomName()
await page.getByLabel("name").fill(name)
for (const buildParameter of buildParameters) {
const richParameter = richParameters.find(
(richParam) => richParam.name === buildParameter.name,
)
if (!richParameter) {
throw new Error(
"build parameter is expected to be present in rich parameter schema",
)
}
const parameterLabel = await page.waitForSelector(
"[data-testid='parameter-field-" + richParameter.name + "']",
{ state: "visible" },
)
if (richParameter.type === "bool") {
const parameterField = await parameterLabel.waitForSelector(
"[data-testid='parameter-field-bool'] .MuiRadio-root input[value='" +
buildParameter.value +
"']",
)
await parameterField.check()
} else if (richParameter.options.length > 0) {
const parameterField = await parameterLabel.waitForSelector(
"[data-testid='parameter-field-options'] .MuiRadio-root input[value='" +
buildParameter.value +
"']",
)
await parameterField.check()
} else if (richParameter.type === "list(string)") {
throw new Error("not implemented yet") // FIXME
} else {
// text or number
const parameterField = await parameterLabel.waitForSelector(
"[data-testid='parameter-field-text'] input",
)
await parameterField.fill(buildParameter.value)
}
}
await page.getByTestId("form-submit").click()
await expect(page).toHaveURL("/@admin/" + name)
await page.getByTestId("build-status").isVisible()
await page.waitForSelector("[data-testid='build-status']", {
state: "visible",
})
return name
}
export const verifyParameters = async (
page: Page,
workspaceName: string,
richParameters: RichParameter[],
expectedBuildParameters: WorkspaceBuildParameter[],
) => {
await page.goto("/@admin/" + workspaceName + "/settings/parameters", {
waitUntil: "networkidle",
})
await expect(page).toHaveURL(
"/@admin/" + workspaceName + "/settings/parameters",
)
for (const buildParameter of expectedBuildParameters) {
const richParameter = richParameters.find(
(richParam) => richParam.name === buildParameter.name,
)
if (!richParameter) {
throw new Error(
"build parameter is expected to be present in rich parameter schema",
)
}
const parameterLabel = await page.waitForSelector(
"[data-testid='parameter-field-" + richParameter.name + "']",
{ state: "visible" },
)
const muiDisabled = richParameter.mutable ? "" : ".Mui-disabled"
if (richParameter.type === "bool") {
const parameterField = await parameterLabel.waitForSelector(
"[data-testid='parameter-field-bool'] .MuiRadio-root.Mui-checked" +
muiDisabled +
" input",
)
const value = await parameterField.inputValue()
expect(value).toEqual(buildParameter.value)
} else if (richParameter.options.length > 0) {
const parameterField = await parameterLabel.waitForSelector(
"[data-testid='parameter-field-options'] .MuiRadio-root.Mui-checked" +
muiDisabled +
" input",
)
const value = await parameterField.inputValue()
expect(value).toEqual(buildParameter.value)
} else if (richParameter.type === "list(string)") {
throw new Error("not implemented yet") // FIXME
} else {
// text or number
const parameterField = await parameterLabel.waitForSelector(
"[data-testid='parameter-field-text'] input" + muiDisabled,
)
const value = await parameterField.inputValue()
expect(value).toEqual(buildParameter.value)
}
}
}
// createTemplate navigates to the /templates/new page and uploads a template
// with the resources provided in the responses argument.
export const createTemplate = async (
@ -401,3 +507,28 @@ const findSessionToken = async (page: Page): Promise<string> => {
}
return sessionCookie.value
}
export const echoResponsesWithParameters = (
richParameters: RichParameter[],
): EchoProvisionerResponses => {
return {
plan: [
{
complete: {
parameters: richParameters,
},
},
],
apply: [
{
complete: {
resources: [
{
name: "example",
},
],
},
},
],
}
}

138
site/e2e/parameters.ts Normal file
View File

@ -0,0 +1,138 @@
import { RichParameter } from "./provisionerGenerated"
// Rich parameters
const emptyParameter: RichParameter = {
name: "",
description: "",
type: "",
mutable: false,
defaultValue: "",
icon: "",
options: [],
validationRegex: "",
validationError: "",
validationMin: undefined,
validationMax: undefined,
validationMonotonic: "",
required: false,
displayName: "",
order: 0,
ephemeral: false,
}
// firstParameter is mutable string with a default value (parameter value not required).
export const firstParameter: RichParameter = {
...emptyParameter,
name: "first_parameter",
displayName: "First parameter",
type: "number",
options: [],
description: "This is first parameter.",
icon: "/emojis/1f310.png",
defaultValue: "123",
mutable: true,
order: 1,
}
// secondParameter is immutable string with a default value (parameter value not required).
export const secondParameter: RichParameter = {
...emptyParameter,
name: "second_parameter",
displayName: "Second parameter",
type: "string",
options: [],
description: "This is second parameter.",
defaultValue: "abc",
icon: "",
order: 2,
}
// thirdParameter is mutable string with an empty default value (parameter value not required).
export const thirdParameter: RichParameter = {
...emptyParameter,
name: "third_parameter",
type: "string",
options: [],
description: "This is third parameter.",
defaultValue: "",
mutable: true,
order: 3,
}
// fourthParameter is immutable boolean with a default "true" value (parameter value not required).
export const fourthParameter: RichParameter = {
...emptyParameter,
name: "fourth_parameter",
type: "bool",
options: [],
description: "This is fourth parameter.",
defaultValue: "true",
icon: "",
order: 3,
}
// fifthParameter is immutable "string with options", with a default option selected (parameter value not required).
export const fifthParameter: RichParameter = {
...emptyParameter,
name: "fifth_parameter",
displayName: "Fifth parameter",
type: "string",
options: [
{
name: "ABC",
description: "This is ABC",
value: "abc",
icon: "",
},
{
name: "DEF",
description: "This is DEF",
value: "def",
icon: "",
},
{
name: "GHI",
description: "This is GHI",
value: "ghi",
icon: "",
},
],
description: "This is fifth parameter.",
defaultValue: "def",
icon: "",
order: 3,
}
// sixthParameter is mutable string without a default value (parameter value is required).
export const sixthParameter: RichParameter = {
...emptyParameter,
name: "sixth_parameter",
displayName: "Sixth parameter",
type: "number",
options: [],
description: "This is sixth parameter.",
icon: "/emojis/1f310.png",
required: true,
mutable: true,
order: 1,
}
// seventhParameter is immutable string without a default value (parameter value is required).
export const seventhParameter: RichParameter = {
...emptyParameter,
name: "seventh_parameter",
displayName: "Seventh parameter",
type: "string",
options: [],
description: "This is seventh parameter.",
required: true,
order: 1,
}

View File

@ -1,5 +1,21 @@
import { test } from "@playwright/test"
import { createTemplate, createWorkspace } from "../helpers"
import {
createTemplate,
createWorkspace,
echoResponsesWithParameters,
verifyParameters,
} from "../helpers"
import {
secondParameter,
fourthParameter,
fifthParameter,
firstParameter,
thirdParameter,
seventhParameter,
sixthParameter,
} from "../parameters"
import { RichParameter } from "../provisionerGenerated"
test("create workspace", async ({ page }) => {
const template = await createTemplate(page, {
@ -17,3 +33,85 @@ test("create workspace", async ({ page }) => {
})
await createWorkspace(page, template)
})
test("create workspace with default immutable parameters", async ({ page }) => {
const richParameters: RichParameter[] = [
secondParameter,
fourthParameter,
fifthParameter,
]
const template = await createTemplate(
page,
echoResponsesWithParameters(richParameters),
)
const workspaceName = await createWorkspace(page, template)
await verifyParameters(page, workspaceName, richParameters, [
{ name: secondParameter.name, value: secondParameter.defaultValue },
{ name: fourthParameter.name, value: fourthParameter.defaultValue },
{ name: fifthParameter.name, value: fifthParameter.defaultValue },
])
})
test("create workspace with default mutable parameters", async ({ page }) => {
const richParameters: RichParameter[] = [firstParameter, thirdParameter]
const template = await createTemplate(
page,
echoResponsesWithParameters(richParameters),
)
const workspaceName = await createWorkspace(page, template)
await verifyParameters(page, workspaceName, richParameters, [
{ name: firstParameter.name, value: firstParameter.defaultValue },
{ name: thirdParameter.name, value: thirdParameter.defaultValue },
])
})
test("create workspace with default and required parameters", async ({
page,
}) => {
const richParameters: RichParameter[] = [
secondParameter,
fourthParameter,
sixthParameter,
seventhParameter,
]
const buildParameters = [
{ name: sixthParameter.name, value: "12345" },
{ name: seventhParameter.name, value: "abcdef" },
]
const template = await createTemplate(
page,
echoResponsesWithParameters(richParameters),
)
const workspaceName = await createWorkspace(
page,
template,
richParameters,
buildParameters,
)
await verifyParameters(page, workspaceName, richParameters, [
// user values:
...buildParameters,
// default values:
{ name: secondParameter.name, value: secondParameter.defaultValue },
{ name: fourthParameter.name, value: fourthParameter.defaultValue },
])
})
test("create workspace and overwrite default parameters", async ({ page }) => {
const richParameters: RichParameter[] = [secondParameter, fourthParameter]
const buildParameters = [
{ name: secondParameter.name, value: "AAAAA" },
{ name: fourthParameter.name, value: "false" },
]
const template = await createTemplate(
page,
echoResponsesWithParameters(richParameters),
)
const workspaceName = await createWorkspace(
page,
template,
richParameters,
buildParameters,
)
await verifyParameters(page, workspaceName, richParameters, buildParameters)
})

View File

@ -84,6 +84,7 @@ export const RichParameterInput: FC<RichParameterInputProps> = ({
direction="column"
spacing={size === "small" ? 1.25 : 2}
className={size}
data-testid={`parameter-field-${parameter.name}`}
>
<ParameterLabel id={fieldProps.id} parameter={parameter} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
@ -114,6 +115,7 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
if (isBoolean(parameter)) {
return (
<RadioGroup
data-testid="parameter-field-bool"
className={styles.radioGroup}
defaultValue={parameterValue}
onChange={(event) => {
@ -139,6 +141,7 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
if (parameter.options.length > 0) {
return (
<RadioGroup
data-testid="parameter-field-options"
className={styles.radioGroup}
defaultValue={parameterValue}
onChange={(event) => {
@ -185,6 +188,7 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
return (
<MultiTextField
data-testid="parameter-field-list-of-string"
label={props.label as string}
values={values}
onChange={(values) => {
@ -206,6 +210,7 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
return (
<TextField
{...props}
data-testid="parameter-field-text"
className={styles.textField}
type={parameter.type}
disabled={disabled}