From e14df76d8f4792f02e9594be8e82eb5589cd1252 Mon Sep 17 00:00:00 2001 From: Renata Date: Tue, 30 Jul 2024 12:03:48 +0200 Subject: [PATCH] [E2E] Backporting e2e auth fixes to 3.20 (#5078) * [E2E] Fix auth file playwright and small refactor (#5065) * fix for auth and save state function * small refactor * adding conditional assertion for TC 137 * e2e auth backport with a fix for attr test --- .changeset/pretty-masks-lay.md | 5 ++ playwright/api/basics.ts | 2 +- playwright/pages/appPageThirdparty.ts | 1 + playwright/pages/appsPage.ts | 1 + playwright/pages/shippingRatesPage.ts | 4 +- playwright/tests/apps.spec.ts | 6 +- playwright/tests/attributes.spec.ts | 10 +-- playwright/tests/auth.setup.ts | 65 +++++++++++++------ .../tests/singlePermissions/product.spec.ts | 28 +++++--- .../readonlyAppAccess.spec.ts | 16 +++-- 10 files changed, 89 insertions(+), 49 deletions(-) create mode 100644 .changeset/pretty-masks-lay.md diff --git a/.changeset/pretty-masks-lay.md b/.changeset/pretty-masks-lay.md new file mode 100644 index 00000000000..2f5c9fcf640 --- /dev/null +++ b/.changeset/pretty-masks-lay.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +You can now run E2E tests locally with no issues diff --git a/playwright/api/basics.ts b/playwright/api/basics.ts index e1e75f11c09..e29926e0dc6 100644 --- a/playwright/api/basics.ts +++ b/playwright/api/basics.ts @@ -51,6 +51,6 @@ export class BasicApiService { }); const loginResponseJson = await loginResponse.json(); - return loginResponseJson as ApiResponse; + return loginResponseJson; } } diff --git a/playwright/pages/appPageThirdparty.ts b/playwright/pages/appPageThirdparty.ts index 39fc41ee994..dd05fa5c646 100644 --- a/playwright/pages/appPageThirdparty.ts +++ b/playwright/pages/appPageThirdparty.ts @@ -26,6 +26,7 @@ export class AppPage extends BasePage { const appUrl = URL_LIST.apps + appId; await this.page.goto(appUrl); + await this.pageHeader.waitFor({ state: "visible", timeout: 10000 }); } async clickAppSettingsButton() { diff --git a/playwright/pages/appsPage.ts b/playwright/pages/appsPage.ts index 040c2f1a99d..172f3e1597f 100644 --- a/playwright/pages/appsPage.ts +++ b/playwright/pages/appsPage.ts @@ -30,6 +30,7 @@ export class AppsPage extends BasePage { async gotoAppsList() { await this.page.goto(URL_LIST.apps); + await this.waitForDOMToFullyLoad(); } async typeManifestUrl(manifestUrl: string) { diff --git a/playwright/pages/shippingRatesPage.ts b/playwright/pages/shippingRatesPage.ts index c7bc0ea4e84..9db6a36f6b2 100644 --- a/playwright/pages/shippingRatesPage.ts +++ b/playwright/pages/shippingRatesPage.ts @@ -52,9 +52,7 @@ export class ShippingRatesPage extends BasePage { await this.assignDialogProductRow.filter({ hasText: name }).waitFor({ state: "visible" }); await this.assignProductsDialog.selectProduct(name); await expect(this.assignProductsDialog.assignAndSaveButton).toBeEnabled(); - await this.waitForNetworkIdleAfterAction(() => - this.assignProductsDialog.assignAndSaveButton.click(), - ); + await this.assignProductsDialog.assignAndSaveButton.click(); await this.assignProductsDialog.assignAndSaveButton.waitFor({ state: "hidden", timeout: 5000, diff --git a/playwright/tests/apps.spec.ts b/playwright/tests/apps.spec.ts index 367e387bb37..e7f86228543 100644 --- a/playwright/tests/apps.spec.ts +++ b/playwright/tests/apps.spec.ts @@ -20,7 +20,6 @@ test.skip("TC: SALEOR_119 User should be able to install and configure app from page, }) => { await appsPage.gotoAppsList(); - await appsPage.waitForDOMToFullyLoad(); await expect(appsPage.installExternalAppButton).toBeVisible(); await appsPage.installExternalAppButton.click(); await appsPage.typeManifestUrl("https://klaviyo.saleor.app/api/manifest"); @@ -50,10 +49,7 @@ test.skip("TC: SALEOR_119 User should be able to install and configure app from await appsPage.expectSuccessBanner(); }); test("TC: SALEOR_120 User should be able to delete thirdparty app @e2e", async () => { - await appPage.waitForNetworkIdleAfterAction(() => - appPage.goToExistingAppPage(APPS.appToBeDeleted.id), - ); - await appPage.pageHeader.waitFor({ state: "visible", timeout: 10000 }); + await appPage.goToExistingAppPage(APPS.appToBeDeleted.id); await expect(appPage.pageHeader).toContainText("Saleor QA App"); await appPage.deleteButton.click(); await appPage.deleteAppDialog.clickDeleteButton(); diff --git a/playwright/tests/attributes.spec.ts b/playwright/tests/attributes.spec.ts index f3441c997d8..979ddba0835 100644 --- a/playwright/tests/attributes.spec.ts +++ b/playwright/tests/attributes.spec.ts @@ -19,7 +19,7 @@ const attributeClasses = ["PRODUCT_TYPE", "PAGE_TYPE"]; for (const attr of attributeClasses) { for (const type of ATTRIBUTES.attributeTypesWithAbilityToAddValues.names) { - const uniqueSlug = `${attr}-${type.replace(" ", "-")}-${SALEOR_124_uuid}`; + const uniqueSlug = `${attr}-${type}-${SALEOR_124_uuid}`.replace(/\s+/g, "-"); test(`TC: SALEOR_124 User should be able to create ${attr} ${type} attribute with ability to add values, required, public @e2e @attributes`, async ({ page, @@ -53,7 +53,7 @@ const SALEOR_125_uuid = faker.datatype.uuid(); for (const attr of attributeClasses) { for (const type of ATTRIBUTES.attributeTypesWithoutAbilityToAddValues.names) { - const uniqueSlug = `${attr}-${type.replace(" ", "-")}-${SALEOR_125_uuid}`; + const uniqueSlug = `${attr}-${type}-${SALEOR_125_uuid}`.replace(/\s+/g, "-"); test(`TC: SALEOR_125 User should be able to create ${attr} ${type} attribute without ability to add values, NOT required, private @e2e @attributes`, async ({ page, @@ -88,7 +88,7 @@ const SALEOR_126_uuid = faker.datatype.uuid(); for (const attr of attributeClasses) { for (const entity of ATTRIBUTES.attributeReferencesEntities.names) { - const uniqueSlug = `${attr}-${entity.replaceAll(" ", "-")}-${SALEOR_126_uuid}`; + const uniqueSlug = `${attr}-${entity}-${SALEOR_126_uuid}`.replace(/\s+/g, "-"); test(`TC: SALEOR_126 User should be able to create ${attr} References attribute for ${entity}, NOT required, public @e2e @attributes`, async ({ page, @@ -101,7 +101,7 @@ for (const attr of attributeClasses) { await attributesPage.selectAttributeType(attr); await attributesPage.typeAttributeDefaultLabel(`${attr} - REFERENCES for ${entity}`); await attributesPage.fillAttributeSlug(uniqueSlug); - await attributesPage.selectAttributeInputType("Reference"); + await attributesPage.selectAttributeInputType("REFERENCE"); await attributesPage.selectAttributeEntityType(entity); await attributesPage.clickValueRequiredCheckbox(); await attributesPage.waitForNetworkIdleAfterAction(() => attributesPage.clickSaveButton()); @@ -172,6 +172,8 @@ for (const attr of ATTRIBUTES.attributesToBeUpdated) { await attributesPage.fillMetadataFields("new key", "new value"); await attributesPage.waitForNetworkIdleAfterAction(() => attributesPage.clickSaveButton()); await attributesPage.expectSuccessBanner(); + await attributesPage.expectElementIsHidden(attributesPage.successBanner); + await attributesPage.attributeSelect.waitFor({ state: "visible" }); await expect(attributesPage.attributeSelect).toHaveAttribute("aria-disabled", "true"); await expect(attributesPage.metadataKeyInput).toHaveValue("new key"); await expect(attributesPage.metadataValueInput).toHaveValue("new value"); diff --git a/playwright/tests/auth.setup.ts b/playwright/tests/auth.setup.ts index 71929959873..403b07b38f5 100644 --- a/playwright/tests/auth.setup.ts +++ b/playwright/tests/auth.setup.ts @@ -1,11 +1,24 @@ import { BasicApiService } from "@api/basics"; -import { permissions, USER_PERMISSION, UserPermissionType } from "@data/userPermissions"; -import { APIRequestContext, test as setup } from "@playwright/test"; +import { permissions, USER_PERMISSION } from "@data/userPermissions"; +import { APIRequestContext, expect, test as setup } from "@playwright/test"; import fs from "fs"; import path from "path"; setup.describe.configure({ mode: "serial" }); +const removeAuthFolder = () => { + const authDir = path.join(__dirname, "../.auth"); + + if (fs.existsSync(authDir)) { + fs.rmSync(authDir, { recursive: true }); + console.log(".auth folder removed"); + } +}; + +setup.beforeAll(() => { + removeAuthFolder(); +}); + const authenticateAndSaveState = async ( request: APIRequestContext, email: string, @@ -14,19 +27,32 @@ const authenticateAndSaveState = async ( ) => { const basicApiService = new BasicApiService(request); - await basicApiService.logInUserViaApi({ email, password }); + const loginResponse = await basicApiService.logInUserViaApi({ email, password }); + const errors = loginResponse.data.tokenCreate.errors; + + if ( + setup.info().title === + "TC: SALEOR_137 Admin User should be able to deactivate other user @e2e @staff-members" + ) { + await expect(errors[0].code).toEqual("INACTIVE"); + } else { + await expect(errors).toEqual([]); + } const loginJsonInfo = await request.storageState(); - loginJsonInfo.origins.push({ - origin: process.env.BASE_URL!, - localStorage: [ - { - name: "_saleorRefreshToken", - value: loginJsonInfo.cookies[0].value, - }, - ], - }); + loginJsonInfo.origins = [ + { + origin: process.env.BASE_URL!, + localStorage: [ + { + name: "_saleorRefreshToken", + value: loginResponse.data.tokenCreate.refreshToken, + }, + ], + }, + ]; + fs.writeFileSync(filePath, JSON.stringify(loginJsonInfo, null, 2)); }; const authSetup = async ( @@ -57,11 +83,12 @@ setup("Authenticate as admin via API", async ({ request }) => { ); }); -const user: UserPermissionType = USER_PERMISSION; -const password: string = process.env.E2E_PERMISSIONS_USERS_PASSWORD!; +setup("Authenticate permission users via API", async ({ request }) => { + for (const permission of permissions) { + const email = USER_PERMISSION[permission]; + const password = process.env.E2E_PERMISSIONS_USERS_PASSWORD!; + const fileName = `${permission}.json`; -for (const permission of permissions) { - setup(`Authenticate as ${permission} user via API`, async ({ request }) => { - await authSetup(request, user[permission], password, `${permission}.json`); - }); -} + await authSetup(request, email, password, fileName); + } +}); diff --git a/playwright/tests/singlePermissions/product.spec.ts b/playwright/tests/singlePermissions/product.spec.ts index 09d1acba260..e2c20f1a94b 100644 --- a/playwright/tests/singlePermissions/product.spec.ts +++ b/playwright/tests/singlePermissions/product.spec.ts @@ -3,42 +3,50 @@ import { CollectionsPage } from "@pages/collectionsPage"; import { HomePage } from "@pages/homePage"; import { MainMenuPage } from "@pages/mainMenuPage"; import { ProductPage } from "@pages/productPage"; -import { expect, test } from "@playwright/test"; - -test.use({ storageState: "playwright/.auth/product.json" }); +import { BrowserContext, expect, test } from "@playwright/test"; +let context: BrowserContext; let home: HomePage; let mainMenuPage: MainMenuPage; let productPage: ProductPage; let categoriesPage: CategoriesPage; let collectionsPage: CollectionsPage; -test.beforeEach(({ page }) => { +test.beforeEach(async ({ browser }) => { + context = await browser.newContext({ + storageState: "playwright/.auth/product.json", + }); + + const page = await context.newPage(); + productPage = new ProductPage(page); home = new HomePage(page); mainMenuPage = new MainMenuPage(page); categoriesPage = new CategoriesPage(page); collectionsPage = new CollectionsPage(page); -}); -test("TC: SALEOR_23 User should be able to navigate to product list as a staff member using PRODUCT permission @e2e", async () => { + await home.goto(); await home.welcomeMessage.waitFor({ state: "visible", timeout: 30000 }); +}); + +test.afterEach(async () => { + await context.close(); +}); + +test("TC: SALEOR_23 User should be able to navigate to product list as a staff member using PRODUCT permission @e2e", async () => { await mainMenuPage.openProducts(); await expect(productPage.addProductButton).toBeVisible(); await mainMenuPage.expectMenuItemsCount(6); await productPage.expectGridToBeAttached(); }); + test("TC: SALEOR_24 User should be able to navigate to collections list as a staff member using PRODUCT permission @e2e", async () => { - await home.goto(); - await home.welcomeMessage.waitFor({ state: "visible", timeout: 30000 }); await mainMenuPage.openCollections(); await expect(collectionsPage.createCollectionButton).toBeVisible(); await mainMenuPage.expectMenuItemsCount(6); await collectionsPage.expectGridToBeAttached(); }); test("TC: SALEOR_25 User should be able to navigate to categories list as a staff member using PRODUCT permission @e2e", async () => { - await home.goto(); - await home.welcomeMessage.waitFor({ state: "visible", timeout: 30000 }); await mainMenuPage.openCategories(); await expect(categoriesPage.createCategoryButton).toBeVisible(); await mainMenuPage.expectMenuItemsCount(6); diff --git a/playwright/tests/singlePermissions/readonlyAppAccess.spec.ts b/playwright/tests/singlePermissions/readonlyAppAccess.spec.ts index c7a1778a096..b12657aa41d 100644 --- a/playwright/tests/singlePermissions/readonlyAppAccess.spec.ts +++ b/playwright/tests/singlePermissions/readonlyAppAccess.spec.ts @@ -1,8 +1,8 @@ -import { URL_LIST } from "@data/url"; import { permissions } from "@data/userPermissions"; import { AppDetailsPage } from "@pages/appDetailsPage"; import { AppPage } from "@pages/appPageThirdparty"; import { AppsPage } from "@pages/appsPage"; +import { HomePage } from "@pages/homePage"; import { MainMenuPage } from "@pages/mainMenuPage"; import { expect, test } from "@playwright/test"; @@ -11,17 +11,19 @@ const permissionList = permissions.filter(item => item !== permissionToExclude); for (const permission of permissionList) { test.use({ storageState: `playwright/.auth/${permission}.json` }); - test(`TC: SALEOR_131 User with ${permission} permissions should have readonly access to Apps @e2e @appp`, async ({ + test(`TC: SALEOR_131 User with ${permission} permissions should have readonly access to Apps @e2e @app`, async ({ page, }) => { + const home = new HomePage(page); const mainMenuPage = new MainMenuPage(page); const appsPage = new AppsPage(page); const appPage = new AppPage(page); const appDetailsPage = new AppDetailsPage(page); - await page.goto(URL_LIST.homePage); - await mainMenuPage.waitForNetworkIdleAfterAction(() => mainMenuPage.openApps()); - await mainMenuPage.waitForDOMToFullyLoad(); + await home.goto(); + await home.welcomeMessage.waitFor({ state: "visible", timeout: 30000 }); + await mainMenuPage.openApps(); + await expect(appsPage.installExternalAppButton).not.toBeVisible(); const appLists = [ @@ -34,9 +36,9 @@ for (const permission of permissionList) { await appsPage.waitForDOMToFullyLoad(); await expect(appList).toBeVisible(); } - await appsPage.waitForNetworkIdleAfterAction(() => appsPage.installedAppRow.first().click()); + await appsPage.installedAppRow.first().click(); await expect(appPage.appSettingsButton).toBeVisible(); - await appsPage.waitForNetworkIdleAfterAction(() => appPage.appSettingsButton.click()); + await appPage.appSettingsButton.click(); await expect(appDetailsPage.appDetailsSection).toBeVisible(); const buttons = [