From 810ab4cb37864bca14c5fb12360f1dfe51717066 Mon Sep 17 00:00:00 2001 From: Neven Dyulgerov Date: Mon, 27 May 2024 21:12:40 +0300 Subject: [PATCH 1/4] feat(apps/web): Add file --- apps/web/e2e/pages/inputs.spec.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/web/e2e/pages/inputs.spec.ts diff --git a/apps/web/e2e/pages/inputs.spec.ts b/apps/web/e2e/pages/inputs.spec.ts new file mode 100644 index 000000000..e69de29bb From f03da3a5b7ef0d968d7aa80aa73e800b1cffe18d Mon Sep 17 00:00:00 2001 From: Neven Dyulgerov Date: Mon, 27 May 2024 21:13:00 +0300 Subject: [PATCH 2/4] test(apps/web): Add e2e tests for inputs page --- apps/web/e2e/pages/inputs.spec.ts | 82 +++++++++++++++++++++ apps/web/src/components/inputs/inputRow.tsx | 1 + 2 files changed, 83 insertions(+) diff --git a/apps/web/e2e/pages/inputs.spec.ts b/apps/web/e2e/pages/inputs.spec.ts index e69de29bb..46296912b 100644 --- a/apps/web/e2e/pages/inputs.spec.ts +++ b/apps/web/e2e/pages/inputs.spec.ts @@ -0,0 +1,82 @@ +import { expect, test } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.goto("inputs"); +}); + +test("should have correct page title", async ({ page }) => { + await expect(page).toHaveTitle(/Inputs \| CartesiScan/); +}); + +test("should have correct title", async ({ page }) => { + const title = page.getByRole("heading", { name: "Inputs" }); + await expect(title.first()).toBeVisible(); +}); + +test("should display 'All inputs' table", async ({ page }) => { + await expect( + page.getByRole("row", { name: "From To Method Index Age Data" }), + ).toBeVisible(); + await expect(page.getByRole("row")).toHaveCount(31); +}); + +test("should open application inputs page", async ({ page }) => { + await expect(page.getByTestId("inputs-table-spinner")).not.toBeVisible(); + const applicationSummaryLinks = page + .getByTestId("application-inputs-link") + .getByRole("link"); + + const firstLink = applicationSummaryLinks.first(); + const href = (await firstLink.getAttribute("href")) as string; + const [hrefPart] = href + .split(/applications|inputs|\//) + .filter((p) => p !== ""); + + await firstLink.click(); + + await page.waitForURL(`/applications/${hrefPart}/inputs`); +}); + +test("should open input details", async ({ page }) => { + await expect(page.getByTestId("inputs-table-spinner")).not.toBeVisible(); + const inputRowToggle = await page.getByTestId("input-row-toggle"); + + const firstInputRowToggle = inputRowToggle.first(); + await firstInputRowToggle.click(); + await expect(page.getByText("Notices")).toBeVisible(); + await expect(page.getByText("Reports")).toBeVisible(); + await expect(page.getByText("Vouchers")).toBeVisible(); + await expect(page.getByText("Raw")).toBeVisible(); + await expect(page.getByText("As Text")).toBeVisible(); + await expect(page.getByText("As JSON")).toBeVisible(); +}); + +test("should search for specific input", async ({ page }) => { + await expect(page.getByTestId("inputs-table-spinner")).not.toBeVisible(); + let fromAddress = page + .getByTestId("application-from-address") + .getByRole("paragraph"); + + const firstLink = fromAddress.first(); + const href = (await firstLink.textContent()) as string; + const [addressPrefix] = href.split("..."); + + const search = await page.getByTestId("search-input"); + await search.focus(); + await page.keyboard.type(addressPrefix); + await page.keyboard.press("Enter"); + await page.waitForTimeout(2000); + + fromAddress = page + .getByTestId("application-from-address") + .getByRole("paragraph"); + + const addresses = await fromAddress.all(); + addresses.map(async (address) => { + const linkHref = (await address.textContent()) as string; + + expect( + linkHref.toLowerCase().startsWith(addressPrefix.toLowerCase()), + ).toBe(true); + }); +}); diff --git a/apps/web/src/components/inputs/inputRow.tsx b/apps/web/src/components/inputs/inputRow.tsx index 5fd76d4ff..6cb236790 100644 --- a/apps/web/src/components/inputs/inputRow.tsx +++ b/apps/web/src/components/inputs/inputRow.tsx @@ -105,6 +105,7 @@ const InputRow: FC = ({ alignItems: "center", justifyContent: "center", }} + data-testid="application-inputs-link" >
Date: Thu, 6 Jun 2024 10:57:29 +0300 Subject: [PATCH 3/4] fix: Fix turbo version --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 83c188449..92d5fbea7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,7 +5,7 @@ RUN apk add --no-cache libc6-compat RUN apk update WORKDIR /app -RUN yarn global add turbo +RUN yarn global add turbo@1.13.3 COPY . . RUN turbo prune --scope=web --docker From 34066e1107f452d960329507a1c0fa81f332d581 Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Tue, 25 Jun 2024 16:06:14 +1200 Subject: [PATCH 4/4] feat(e2e): Add fixture to intercept graphql calls for rollups graphql api by Operation name. Trust on nodes running makes the e2e tests brittle. Therefore, any call to a Rollups graphQL API will get a mocked response for better control. --- apps/web/codegen.ts | 10 +++++- apps/web/e2e/fixtures/test.ts | 50 ++++++++++++++++++++++++++ apps/web/e2e/pages/connections.spec.ts | 12 +++++-- apps/web/e2e/utils/checkStatus.data.ts | 14 ++++++++ apps/web/e2e/utils/connection.ts | 2 +- apps/web/package.json | 1 + yarn.lock | 9 +++++ 7 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 apps/web/e2e/fixtures/test.ts create mode 100644 apps/web/e2e/utils/checkStatus.data.ts diff --git a/apps/web/codegen.ts b/apps/web/codegen.ts index 194e5d7b3..992699ff6 100644 --- a/apps/web/codegen.ts +++ b/apps/web/codegen.ts @@ -47,12 +47,20 @@ const generateConfig = (configs: CommonConfig[]): Generates => { schema, documents, preset: "import-types", - plugins: ["typescript-operations", "typescript-urql"], + plugins: [ + "typescript-operations", + "typescript-urql", + "named-operations-object", + ], presetConfig: { typesPath: `.${sep}types`, }, config: { withHooks: false, + // named-operations-object plugin configs + enumAsTypes: true, + useConsts: true, + identifierName: "allOperations", }, }, [join(path, "hooks", "queries.tsx")]: { diff --git a/apps/web/e2e/fixtures/test.ts b/apps/web/e2e/fixtures/test.ts new file mode 100644 index 000000000..1aa259a00 --- /dev/null +++ b/apps/web/e2e/fixtures/test.ts @@ -0,0 +1,50 @@ +import { Page, Route, test as baseTest } from "@playwright/test"; + +/** + * For validations purposes if needed. e.g. check if API + * was called correctly. + */ +type CalledWith = Record; + +/** + * Function to register as an interceptor. + * Interceptions are per-operation, so multiple can be registered without the risk + * of overwriting one another. + * @param page {Page} playwright page client. + * @param operationName Graphql operation name. + * @param resp The expected return for the mock. + * @returns + */ +export async function interceptGQL( + page: Page, + operationName: string, + resp: Record, +): Promise { + const reqs: CalledWith[] = []; + + await page.route("**/graphql", function (route: Route) { + const req = route.request().postDataJSON(); + + // Pass along to the previous handler in case the operation does not match. + if (req.operationName !== operationName) { + return route.fallback(); + } + + // Store what variables the API was called with; + reqs.push(req.variables); + + return route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ data: resp }), + }); + }); + + return reqs; +} + +export const test = baseTest.extend<{ interceptGQL: typeof interceptGQL }>({ + interceptGQL: async ({ browser }, use) => { + await use(interceptGQL); + }, +}); diff --git a/apps/web/e2e/pages/connections.spec.ts b/apps/web/e2e/pages/connections.spec.ts index 529b547a3..13fd2551b 100644 --- a/apps/web/e2e/pages/connections.spec.ts +++ b/apps/web/e2e/pages/connections.spec.ts @@ -1,9 +1,17 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; import { Address } from "viem"; +import { allOperations } from "../../src/graphql/rollups/operations"; +import { test } from "../fixtures/test"; +import { checkStatusSuccessResponse } from "../utils/checkStatus.data"; import { createConnection } from "../utils/connection"; -test.beforeEach(async ({ page }) => { +test.beforeEach(async ({ page, interceptGQL }) => { await page.goto("/connections"); + await interceptGQL( + page, + allOperations.Query.checkStatus, + checkStatusSuccessResponse, + ); }); test("should have correct page title", async ({ page }) => { diff --git a/apps/web/e2e/utils/checkStatus.data.ts b/apps/web/e2e/utils/checkStatus.data.ts new file mode 100644 index 000000000..6b05b7085 --- /dev/null +++ b/apps/web/e2e/utils/checkStatus.data.ts @@ -0,0 +1,14 @@ +export const checkStatusSuccessResponse = { + inputs: { + totalCount: 41, + }, + vouchers: { + totalCount: 0, + }, + reports: { + totalCount: 41, + }, + notices: { + totalCount: 0, + }, +}; diff --git a/apps/web/e2e/utils/connection.ts b/apps/web/e2e/utils/connection.ts index 08025cec4..f63fda549 100644 --- a/apps/web/e2e/utils/connection.ts +++ b/apps/web/e2e/utils/connection.ts @@ -4,7 +4,7 @@ import { Address } from "viem"; export const createConnection = async ( page: Page, address: Address, - url = "https://honeypot.sepolia.rollups.staging.cartesi.io/graphql", + url = "http://rollups-mocked.calls.to/graphql", ) => { // Find and click the button for displaying the connection modal const button = page.getByTestId("add-connection"); diff --git a/apps/web/package.json b/apps/web/package.json index 38be91e55..ebb38dcba 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -50,6 +50,7 @@ "@graphql-codegen/cli": "^5", "@graphql-codegen/client-preset": "^4", "@graphql-codegen/import-types-preset": "^3.0.0", + "@graphql-codegen/named-operations-object": "^3.0.0", "@graphql-codegen/typed-document-node": "^5", "@graphql-codegen/typescript": "^4", "@graphql-codegen/typescript-operations": "^4", diff --git a/yarn.lock b/yarn.lock index 26503b1ce..82218281d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2262,6 +2262,15 @@ "@graphql-codegen/visitor-plugin-common" "2.13.1" tslib "~2.6.0" +"@graphql-codegen/named-operations-object@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/named-operations-object/-/named-operations-object-3.0.0.tgz#2b5f718ffcfdd3202243c4a83bbc727cd914c8be" + integrity sha512-M9MKeTd7GINJQDV2iDvVjOJrLusU9om9rkizl6+z7xRiABA1cVz7s/trwi1hRHOpAE7Qe/k7ykRopRHysqzw5A== + dependencies: + "@graphql-codegen/plugin-helpers" "^3.0.0" + change-case-all "1.0.15" + tslib "~2.6.0" + "@graphql-codegen/plugin-helpers@^2.7.2": version "2.7.2" resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.7.2.tgz#6544f739d725441c826a8af6a49519f588ff9bed"