Всем привет! Важная ремарка - это моя первая тема, а также я только начинаю погружаться в автоматизацию, с нуля внедряю в проект, где ее никогда не было, так что опыта у меня мало.
Столкнулся с проблемой циклических импортов в своей архитектуре. Пример кода будет с сайта https://www.saucedemo.com/, чтобы многие были в контексте.
Вводные:
- В случае с упомянутым сайтом у нас есть несколько страниц:
LoginPage
,InventoryPage
,CartPage
и тд. - Также есть компоненты, которые хочется сделать в виде отдельных классов:
BurgerMenu
,Header
Варианты реализации, которые вижу я:
- Вариант, который чаще всего вижу в интернетах: С помощью композиции объявить
Header
вInventoryPage
,CartPage
и тд - Написать публичные методы, которые будут обращаться к этим компонентам
Пример такой страницы:
// inventory-page.ts
import { MainHeader } from "../../common/components/main-header";
import { BasePage } from "../../common/pages/base-page";
import { CartPage } from "../../cart/pages/cart-page";
export class InventoryPage extends BasePage {
protected readonly url = '/inventory.html';
private readonly header = new MainHeader(this.page);
public async openCartPage(): Promise<CartPage> {
await this.header.clickOnCart();
return new CartPage(this.page);
}
}
Пример части кода теста
const cartPage = await inventoryPage.openCartPage();
Вроде бы неплохо, но что лично сразу смущает меня - написание отдельного метода на уровне страницы, у общего для нескольких страниц компонента. Сразу пришло в голову сделать header публичным и не писать лишний метод:
// inventory-page.ts
import { MainHeader } from "../../common/components/main-header";
import { BasePage } from "../../common/pages/base-page";
export class InventoryPage extends BasePage {
protected readonly url = '/inventory.html';
public readonly header = new MainHeader(this.page);
}
Пример части кода теста
const cartPage = await inventoryPage.header.clickOnCart();
Лично такой вариант мне нравится больше, т.к. теперь я могу использовать clickOnCart
у любой страницы, у которой есть header
.
Дальше я решил указать Header
в BasePage
// base-page.ts
import test from "@playwright/test";
import { BasePageObject } from "../base-page-object";
import { MainHeader } from "../components/main-header";
export abstract class BasePage extends BasePageObject {
protected abstract url: string;
public readonly header = new MainHeader(this.page);
public async open(): Promise<void> {
await test.step(`Go to url "${this.baseUrl}${this.url}"`, async () => {
await this.page.goto(this.url);
});
}
private get baseUrl(): string {
return test.info().project.use.baseURL as string;
}
}
Теперь метод clickOnCart
будет у всех страниц, которые наследуются от BasePage
.
НО! Теперь я сталкиваюсь с циклическими импортами, т.к.
-
BasePage
зависит отMainHeader
-
MainHeader
зависит отCartPage
, потому чтоclickOnCart
возращает его экземпляр -
CartPage
зависит отBasePage
-
BasePage
зависит отMainHeader
Вот и вопрос: как вы продумываете работу с компонентами? В случае с простым приложением понятно - прокинул везде вручную и забыл, но если таких общих компонентов много, то как быть?