Всем привет! Важная ремарка - это моя первая тема, а также я только начинаю погружаться в автоматизацию, с нуля внедряю в проект, где ее никогда не было, так что опыта у меня мало.
Столкнулся с проблемой циклических импортов в своей архитектуре. Пример кода будет с сайта 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
Вот и вопрос: как вы продумываете работу с компонентами? В случае с простым приложением понятно - прокинул везде вручную и забыл, но если таких общих компонентов много, то как быть?