Skip to content

Commit

Permalink
Added an e2e test to the playwright ui tests (#3950)
Browse files Browse the repository at this point in the history
  • Loading branch information
EricWittmann authored Nov 7, 2023
1 parent 3897c2a commit 2798984
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 46 deletions.
13 changes: 13 additions & 0 deletions ui/tests/specs/data/openapi-simple-invalid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const OPENAPI_DATA_INVALID = {
"openapi": "3.0.2",
"info": {
"title": "Empty API Spec",
"description": "A minimal spec",
"version": "1.0"
},
"paths": {
"/": {
"get": {}
}
}
};
10 changes: 10 additions & 0 deletions ui/tests/specs/data/openapi-simple-v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const OPENAPI_DATA_V2 = {
"openapi": "3.0.2",
"info": {
"title": "Empty API Spec",
"description": "A minimal spec",
"version": "2.0"
},
"paths": {
}
};
10 changes: 10 additions & 0 deletions ui/tests/specs/data/openapi-simple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const OPENAPI_DATA = {
"openapi": "3.0.2",
"info": {
"title": "Empty API Spec",
"description": "A minimal spec",
"version": "1.0"
},
"paths": {
}
};
117 changes: 117 additions & 0 deletions ui/tests/specs/e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { test, expect } from "@playwright/test";
import { OPENAPI_DATA } from "./data/openapi-simple";
import { OPENAPI_DATA_V2 } from "./data/openapi-simple-v2";

const OPENAPI_DATA_STR: string = JSON.stringify(OPENAPI_DATA, null, 4);
const OPENAPI_DATA_V2_STR: string = JSON.stringify(OPENAPI_DATA_V2, null, 4);

const REGISTRY_UI_URL: string = process.env["REGISTRY_UI_URL"] || "http://localhost:8888";

test("End to End - Upload artifact", async ({ page }) => {
await page.goto(REGISTRY_UI_URL);
await expect(page).toHaveTitle(/Apicurio Registry/);

expect(page.getByTestId("btn-toolbar-upload-artifact")).toBeDefined();

// Click the "Upload artifact" button
await page.getByTestId("btn-toolbar-upload-artifact").click();
await expect(page.getByTestId("upload-artifact-form-group")).toHaveValue("");

// Upload a new artifact
await page.getByTestId("upload-artifact-form-group").fill("e2e");
await page.getByTestId("upload-artifact-form-id").fill("MyArtifact");
await page.getByTestId("upload-artifact-form-type-select").click();
await page.getByTestId("upload-artifact-form-OPENAPI").click();
await page.locator("#artifact-content").fill(OPENAPI_DATA_STR);
await page.getByTestId("upload-artifact-modal-btn-upload").click();

// Make sure we redirected to the artifact detail page.
await expect(page).toHaveURL(/.+\/artifacts\/e2e\/MyArtifact\/versions\/latest/);

// Assert the meta-data is as expected
await expect(page.getByTestId("artifact-details-name")).toHaveText("Empty API Spec");
await expect(page.getByTestId("artifact-details-id")).toHaveText("MyArtifact");
await expect(page.getByTestId("artifact-details-state")).toHaveText("ENABLED");
await expect(page.getByTestId("artifact-details-labels")).toHaveText("No labels");
await expect(page.getByTestId("artifact-details-properties")).toHaveText("No properties");
});


test("End to End - Edit metadata", async ({ page }) => {
// Navigate to the artifact details page
await page.goto(`${REGISTRY_UI_URL}/artifacts/e2e/MyArtifact/versions/latest`);

// Click the "Edit" button to show the modal
await page.getByTestId("artifact-btn-edit").click();
await expect(page.getByTestId("edit-metadata-modal-name")).toHaveValue("Empty API Spec");

// Change/add some values
await page.getByTestId("edit-metadata-modal-name").fill("My Empty API");
await page.getByTestId("edit-metadata-modal-description").fill("A simple empty API.");
await page.getByTestId("edit-metadata-modal-labels").fill("one, two, three");

// Add a property
await page.getByTestId("edit-metadata-modal-add-property").click();
await page.getByTestId("edit-metadata-modal-property-name-0").fill("some-key");
await page.getByTestId("edit-metadata-modal-property-value-0").fill("some-value");

// Save changes
await page.getByTestId("modal-btn-edit").click();

// Assert the meta-data is as expected
await expect(page.getByTestId("artifact-details-name")).toHaveText("My Empty API");
await expect(page.getByTestId("artifact-details-description")).toHaveText("A simple empty API.");
await expect(page.getByTestId("artifact-details-id")).toHaveText("MyArtifact");
await expect(page.getByTestId("artifact-details-state")).toHaveText("ENABLED");
expect(page.getByTestId("artifact-details-labels").getByText("one")).toBeDefined();
expect(page.getByTestId("artifact-details-labels").getByText("two")).toBeDefined();
expect(page.getByTestId("artifact-details-labels").getByText("three")).toBeDefined();
expect(page.getByTestId("artifact-details-properties").getByText("some-key")).toBeDefined();
expect(page.getByTestId("artifact-details-properties").getByText("some-value")).toBeDefined();
});


test("End to End - Artifact specific rules", async ({ page }) => {
// Navigate to the artifact details page
await page.goto(`${REGISTRY_UI_URL}/artifacts/e2e/MyArtifact/versions/latest`);

await expect(page.locator("div.rule")).toHaveCount(3);
await expect(page.locator("#validity-rule-name")).toContainText("Validity rule");
await expect(page.locator("#compatibility-rule-name")).toContainText("Compatibility rule");
await expect(page.locator("#integrity-rule-name")).toContainText("Integrity rule");

// Enable the Rule
await page.getByTestId("rules-validity-enable").click();
expect(page.getByTestId("rules-validity-config-toggle")).toBeDefined();

// Click the Rule Configuration toggle
await page.getByTestId("rules-validity-config-toggle").click();
expect(page.getByTestId("rules-validity-config-syntaxOnly")).toBeDefined();

// Select "syntax only" config option
await page.getByTestId("validity-config-syntax").click();
expect(page.getByTestId("rules-validity-disable")).toBeDefined();
});


test("End to End - Upload new version", async ({ page }) => {
// Navigate to the artifact details page
await page.goto(`${REGISTRY_UI_URL}/artifacts/e2e/MyArtifact/versions/latest`);

// Upload a new version
await page.getByTestId("header-btn-upload-version").click();
await page.locator("#artifact-content").fill(OPENAPI_DATA_V2_STR);
await page.getByTestId("modal-btn-upload").click();

// Make sure we redirected to the artifact detail page.
await expect(page).toHaveURL(/.+\/artifacts\/e2e\/MyArtifact\/versions\/2/);
});


test("End to End - Delete artifact", async ({ page }) => {
await page.goto(`${REGISTRY_UI_URL}/artifacts/e2e/MyArtifact/versions/latest`);
await page.getByTestId("header-btn-delete").click();
await page.getByTestId("modal-btn-delete").click();

await expect(page).toHaveURL(/.+\/artifacts/);
});
1 change: 1 addition & 0 deletions ui/tests/specs/globalRules.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ test.beforeEach(async ({ page }) => {
});

test("Global Rules - List Rules", async ({ page }) => {
await expect(page.locator("div.rule")).toHaveCount(3);
await expect(page.locator("#validity-rule-name")).toContainText("Validity rule");
await expect(page.locator("#compatibility-rule-name")).toContainText("Compatibility rule");
await expect(page.locator("#integrity-rule-name")).toContainText("Integrity rule");
Expand Down
17 changes: 9 additions & 8 deletions ui/ui-app/src/app/components/common/UrlUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import { IsLoading } from "./IsLoading.tsx";
export type UrlUploadProps = {
id: string | "url-upload";
urlPlaceholder: string | "";
testId?: string;
onChange: (value: string | undefined, url: string | undefined) => void;
};

/**
* A control similar to the FileUpload control from patternfly that allows uploading from
* a URL instead of a file.
*/
export const UrlUpload: FunctionComponent<UrlUploadProps> = ({ id, urlPlaceholder, onChange }: UrlUploadProps) => {
export const UrlUpload: FunctionComponent<UrlUploadProps> = (props: UrlUploadProps) => {
const [url, setUrl] = useState<string>();
const [previewContent, setPreviewContent] = useState<string>();
const [isLoading, setLoading] = useState<boolean>(false);
Expand All @@ -44,7 +45,7 @@ export const UrlUpload: FunctionComponent<UrlUploadProps> = ({ id, urlPlaceholde
setDownloadError(undefined);
setPreviewContent(content);
setLoading(false);
onChange(content, url);
props.onChange(content, url);
}).catch(error => {
setDownloadError(error.message);
setLoading(false);
Expand All @@ -54,7 +55,7 @@ export const UrlUpload: FunctionComponent<UrlUploadProps> = ({ id, urlPlaceholde
const onClear = (): void => {
setUrl("");
setPreviewContent("");
onChange(undefined, undefined);
props.onChange(undefined, undefined);
};

const spinner: React.ReactNode = (
Expand All @@ -65,17 +66,17 @@ export const UrlUpload: FunctionComponent<UrlUploadProps> = ({ id, urlPlaceholde
);

return (
<div className="url-upload">
<div className="url-upload" data-testid={props.testId}>
<div className="url-upload-flex">
<div className="url-upload-url">
<TextInput data-testid={`${id}-input`} value={url} type="text" placeholder={urlPlaceholder} id={id}
<TextInput data-testid={`${props.testId}-input`} value={url} type="text" placeholder={props.urlPlaceholder} id={props.id}
onChange={onTextInputChange} aria-label="url input" />
</div>
<div className="url-fetch-button">
<Button data-testid={`${id}-fetch`} variant="control" isDisabled={!hasUrl()} onClick={onFetch}>Fetch</Button>
<Button data-testid={`${props.testId}-fetch`} variant="control" isDisabled={!hasUrl()} onClick={onFetch}>Fetch</Button>
</div>
<div className="url-clear-button">
<Button data-testid={`${id}-clear`} variant="control" isDisabled={!hasUrl()} onClick={onClear}>Clear</Button>
<Button data-testid={`${props.testId}-clear`} variant="control" isDisabled={!hasUrl()} onClick={onClear}>Clear</Button>
</div>
</div>
<div className="url-upload-preview">
Expand All @@ -91,7 +92,7 @@ export const UrlUpload: FunctionComponent<UrlUploadProps> = ({ id, urlPlaceholde
</div>
</If>
<If condition={!hasError()}>
<TextArea value={previewContent} readOnly={true} />
<TextArea data-testid={`${props.testId}-preview`} value={previewContent} readOnly={true} />
</If>
</IsLoading>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export const EditMetaDataModal: FunctionComponent<EditMetaDataModalProps> = (pro
isRequired={false}
type="text"
id="form-name"
data-testid="form-name"
data-testid="edit-metadata-modal-name"
name="form-name"
aria-describedby="form-name-helper"
value={metaData.name}
Expand All @@ -165,7 +165,7 @@ export const EditMetaDataModal: FunctionComponent<EditMetaDataModalProps> = (pro
<TextArea
isRequired={false}
id="form-description"
data-testid="form-description"
data-testid="edit-metadata-modal-description"
name="form-description"
aria-describedby="form-description-helper"
value={metaData.description}
Expand All @@ -184,7 +184,7 @@ export const EditMetaDataModal: FunctionComponent<EditMetaDataModalProps> = (pro
isRequired={false}
type="text"
id="form-labels"
data-testid="form-labels"
data-testid="edit-metadata-modal-labels"
name="form-labels"
aria-describedby="form-labels-helper"
value={labels}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const PropertiesFormGroup: FunctionComponent<PropertiesFormGroupProps> =
type="text"
placeholder="Enter key"
id={`form-properties-key-${idx}`}
data-testid={`edit-metadata-modal-property-name-${idx}`}
name={`form-properties-key-${idx}`}
validated={property.nameValidated}
value={property.name}
Expand All @@ -67,6 +68,7 @@ export const PropertiesFormGroup: FunctionComponent<PropertiesFormGroupProps> =
<TextInput
type="text"
id={`form-properties-value-${idx}`}
data-testid={`edit-metadata-modal-property-value-${idx}`}
placeholder="Enter value"
name={`form-properties-value-${idx}`}
validated={property.valueValidated}
Expand All @@ -87,7 +89,13 @@ export const PropertiesFormGroup: FunctionComponent<PropertiesFormGroupProps> =
))
}
<GridItem span={12}>
<Button variant="link" icon={<PlusCircleIcon />} className="add-property-button" onClick={() => addArtifactProperty()}>
<Button
variant="link"
icon={<PlusCircleIcon />}
className="add-property-button"
data-testid="edit-metadata-modal-add-property"
onClick={() => addArtifactProperty()}
>
Add property
</Button>{" "}
</GridItem>
Expand Down
Loading

0 comments on commit 2798984

Please sign in to comment.