Skip to content

Commit

Permalink
feat: Finish integration test and make it stable (#281)
Browse files Browse the repository at this point in the history
Because

- #270 

This commit

- Finish integration-test and make it stable
  • Loading branch information
EiffelFly committed Sep 21, 2022
1 parent e82d3bf commit 3fd8d21
Show file tree
Hide file tree
Showing 22 changed files with 508 additions and 557 deletions.
4 changes: 1 addition & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ module.exports = {

// eslint-plugin-storybook
"plugin:storybook/recommended",

// eslint-plugin-testing-library
"plugin:testing-library/react",
],
rules: {
// https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#eslint
Expand All @@ -58,6 +55,7 @@ module.exports = {
extends: [
"plugin:jest/recommended",
"plugin:jest-formatting/recommended",
"plugin:testing-library/react",
],
rules: {
"@typescript-eslint/no-non-null-assertion": "off",
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ node_modules

# testing
/coverage
playwright-state.json

# Build directories (next.js...)
.next/
Expand Down
7 changes: 3 additions & 4 deletions integration-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,17 @@
### About the flaky test

- If the test behavior is related to backend, remember that backend can only handle a request at a time. So if the test run in sequence and the time between requests is too short, the request will fail.
- We have to limit the test worker to one, because the test suite might run to quick to make backend panic.
- We have to limit the test worker to one, because the test suite might run too quick to make backend panic.
- Remember to `make down` backend every time you want have another round of test.
- use `expect().to` after every behavior to make sure the behavior succeeded.
- `page.waitForResponse` is not particularly reliable. If you are facing some flaky test, try to rewrite the whole part with some visual hint, like.
- use `expect().to` after every behavior to make sure the behavior succeeded. But you don't need to use `expect(field).toHaveValue()` after you fill in some text, because it had already beem tested by playwright.
- `page.waitForResponse` is not particularly reliable. If you are facing some flaky test, try to rewrite the whole part with some visual hint, like below.

```js

// waitForResponse is flaky

const saveButton = page.locator("button", { hasText: "Save" });
expect(await saveButton.isEnabled()).toBeTruthy();
const succeedMessage = page.locator("h3", { hasText: "Succeed" });
await Promise.all([saveButton.click(), page.waitForResponse("your url"));

// Rewrite with visual hint
Expand Down
58 changes: 58 additions & 0 deletions integration-test/common/connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Page, expect } from "@playwright/test";

export const expectToDeleteConnector = async (
page: Page,
type: "destination" | "source",
id: string
) => {
await page.goto(`/${type}s/${id}`);

// Should enable open delete model modal button
const openDeleteDestinationModalButton = page.locator("button", {
hasText: "Delete",
});
expect(await openDeleteDestinationModalButton.isEnabled()).toBeTruthy();

// Should open delete destination modal
await openDeleteDestinationModalButton.click();
const deleteResourceModal = page.locator("data-testid=delete-resource-modal");
await expect(deleteResourceModal).toHaveCount(1);

// Should have proper modal title
const modalTitle = deleteResourceModal.locator("h2", {
hasText: `Delete This ${type === "destination" ? "Destination" : "Source"}`,
});
await expect(modalTitle).toHaveCount(1);

// Should have proper confirmation code hint
const confirmationCodeHint = deleteResourceModal.locator("label", {
hasText: `Please type "${id}" to confirm.`,
});
await expect(confirmationCodeHint).toHaveCount(1);

// Should disable delete button
const deleteButton = deleteResourceModal.locator("button", {
hasText: "Delete",
});
expect(await deleteButton.isDisabled()).toBeTruthy();

// Should enable cancel button
const cancelButton = deleteResourceModal.locator("button", {
hasText: "Cancel",
});
expect(await cancelButton.isEnabled()).toBeTruthy();

// Should input correcy confirmation code
const confirmationCodeField =
deleteResourceModal.locator("#confirmationCode");
await confirmationCodeField.type(id);
expect(await deleteButton.isEnabled()).toBeTruthy();

// Should delete destination and navigate to destinations page
await Promise.all([page.waitForNavigation(), deleteButton.click()]);
expect(page.url()).toEqual(`${process.env.NEXT_PUBLIC_MAIN_URL}/${type}s`);

// Should remove the destination from destination/source list
const itemTitle = page.locator("h3", { hasText: id });
await expect(itemTitle).toHaveCount(0);
};
32 changes: 25 additions & 7 deletions integration-test/common/mgmt.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
import { BrowserContext, Page, expect } from "@playwright/test";
import axios from "axios";
import { v4 as uuidv4 } from "uuid";

export const removeRegisteredUser = async () => {
await fetch(
`${process.env.NEXT_PUBLIC_MGMT_BACKEND_BASE_URL}/${process.env.NEXT_PUBLIC_API_VERSION}/local-user`,
{ method: "patch", body: JSON.stringify({ cookie_token: "" }) }
);
try {
await axios.patch(
`${process.env.NEXT_PUBLIC_MGMT_BACKEND_BASE_URL}/${process.env.NEXT_PUBLIC_API_VERSION}/users/local-user`,
{
cookie_token: "",
}
);
} catch (err) {
console.log(err);
}
};

export const addRegisteredUser = async () => {
try {
await axios.patch(
`${process.env.NEXT_PUBLIC_MGMT_BACKEND_BASE_URL}/${process.env.NEXT_PUBLIC_API_VERSION}/users/local-user`,
{
cookie_token: uuidv4(),
}
);
} catch (err) {
console.log(err);
}
};

export const expectToOnboardUser = async (
Expand All @@ -25,9 +46,6 @@ export const expectToOnboardUser = async (
// Shoyld select role
await page.locator("#role").click({ force: true });
await page.locator("#react-select-role-option-0").click();
await expect(page.locator("data-testid=role-selected-option")).toHaveText(
"Manager (who makes decisions)"
);

// Should accept newsletter subscription
await page.locator("#newsletterSubscription").check();
Expand Down
47 changes: 22 additions & 25 deletions integration-test/common/model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//import { Nullable } from "@/types/general";
import { Page, expect, Locator } from "@playwright/test";

export const expectToDeleteModel = async (page: Page, modelId: string) => {
Expand Down Expand Up @@ -177,46 +178,42 @@ export const expectCorrectModelDetails = async ({
export const expectToDeployModel = async (
page: Page,
modelInstanceTag: string,
setupButton: Locator,
timeout?: number
waitForElement: Locator | null,
timeout: number | null
) => {
// Should create model and display model instance section
const modelInstanceTitle = page.locator("h3", {
hasText: "Deploy a model instance",
});
// Should select target model instance tag
const modelInstanceIdOption = page.locator(
"#react-select-modelInstanceId-input"
"#react-select-modelInstanceTag-input"
);
const deployButton = page.locator("button", { hasText: "Deploy" });

await Promise.all([
modelInstanceTitle.isVisible(),
modelInstanceIdOption.isVisible(),
deployButton.isVisible(),
setupButton.click(),
]);

// Should disable deploy button
expect(await deployButton.isDisabled()).toBeTruthy();

// Should select latest model instance
await modelInstanceIdOption.click({ force: true });
await page
.locator("data-testid=modelInstanceId-selected-option", {
.locator("data-testid=modelInstanceTag-selected-option", {
hasText: modelInstanceTag,
})
.click();
await expect(
page.locator("data-testid=modelInstanceId-selected-option")
).toHaveText(modelInstanceTag);

// Should enable deploy button
const deployButton = page.locator("button", { hasText: "Deploy" });
expect(await deployButton.isEnabled()).toBeTruthy();

// Should deploy model
await Promise.all([
page.waitForNavigation({ timeout }),
waitForElement
? waitForElement.waitFor({
state: "visible",
timeout: timeout ? timeout : undefined,
})
: page.waitForNavigation({ timeout: timeout ? timeout : undefined }),
deployButton.click(),
]);

expect(page.url()).toEqual(`${process.env.NEXT_PUBLIC_MAIN_URL}/models`);
};

export type ExpectToSetupLocalModel = {
page: Page;
modelId: string;
modelDescription: string;
modelInstanceTag: string;
waitForElement?: Locator;
};
Loading

0 comments on commit 3fd8d21

Please sign in to comment.