Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[E2E] Backporting e2e auth fixes to 3.20 #5078

Merged
merged 2 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pretty-masks-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

You can now run E2E tests locally with no issues
2 changes: 1 addition & 1 deletion playwright/api/basics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ export class BasicApiService {
});
const loginResponseJson = await loginResponse.json();

return loginResponseJson as ApiResponse<TokenCreateResponse>;
return loginResponseJson;
}
}
1 change: 1 addition & 0 deletions playwright/pages/appPageThirdparty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions playwright/pages/appsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 1 addition & 3 deletions playwright/pages/shippingRatesPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 1 addition & 5 deletions playwright/tests/apps.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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();
Expand Down
10 changes: 6 additions & 4 deletions playwright/tests/attributes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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());
Expand Down Expand Up @@ -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");
Expand Down
65 changes: 46 additions & 19 deletions playwright/tests/auth.setup.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 (
Expand Down Expand Up @@ -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);
}
});
28 changes: 18 additions & 10 deletions playwright/tests/singlePermissions/product.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 9 additions & 7 deletions playwright/tests/singlePermissions/readonlyAppAccess.spec.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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 = [
Expand All @@ -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 = [
Expand Down
Loading