Skip to content

[DON'T MERGE - POC] IBX-11739: Added playwright tests for Trash - #1947

Open
pawlakadrian wants to merge 10 commits into
5.0from
ibx-11739
Open

[DON'T MERGE - POC] IBX-11739: Added playwright tests for Trash#1947
pawlakadrian wants to merge 10 commits into
5.0from
ibx-11739

Conversation

@pawlakadrian

@pawlakadrian pawlakadrian commented Jun 23, 2026

Copy link
Copy Markdown
Contributor
🎫 Issue IBX-11739

Related PRs:

Description:

Sets up Playwright testing for the admin-ui package using the shared cohesivo-playwright library.

  • Added tests/playwright-tests/ directory with playwright.config.ts, tsconfig.json, and package.json
  • Implemented TrashPage and ContentManagementPage page objects
  • Added Trash.spec.ts with 6 tests covering: empty trash, move to trash, restore, restore under new location, delete from trash, and search
  • Added .github/workflows/playwright-tests.yml calling the reusable workflow for all four editions (oss/headless/experience/commerce)

For QA:

Documentation:

@pawlakadrian pawlakadrian changed the title IBX-11739: Added playwright tests for Trash [DON'T MERGE - POC] IBX-11739: Added playwright tests for Trash Jun 23, 2026
Comment thread .github/workflows/playwright-tests.yml Outdated
on:
push:
branches:
- main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be removed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — removed.

export class ContentManagementPage extends AdminUiPage {
readonly udw: UniversalDiscoveryWidget;

constructor(page: Page) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add all css locators to the constructor, so they can be reused and code looks cleaner.

On the other hand - sometimes getBy locators are used, is it possible to switch completely to getBy locators? Copilot suggests something like:

Element | Preferred locator
Button | getByRole()
Link | getByRole()
Checkbox | getByRole()
Text field | getByLabel()
Image | getByAltText()
Static text | getByText()
Custom component | getByTestId()
Loading spinner | CSS
Overlay | CSS
Toast | CSS or getByTestId()
Animation | CSS

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Locators are now hoisted to readonly fields / factory methods so each selector is defined once, and switched to getByRole/getByText

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This page file seems like a mix of different pages and components in Behat notation and some code overlaps with https://github.com/ibexa/cohesivo-playwright/blob/5.0/src/pages/admin/AdminUiPage.ts - .ie https://github.com/ibexa/cohesivo-playwright/blob/5b13038a8e17b0b0e01c99d9d67b765f9e69ff12/src/pages/admin/AdminUiPage.ts#L22 and https://github.com/ibexa/admin-ui/pull/1947/changes#diff-46711842a811d22853c02fa9e2fee52d5502bf751dcae9f71e6994d45139d23eR87 do the same thing.

My recommendation is to scrap https://github.com/ibexa/cohesivo-playwright/blob/5.0/src/pages/admin/AdminUiPage.ts (leave maybe login stuff in ibexa/cohesivo-playwright as login stuff is also implemented in ibexa/behat) and to break this file into proper pages (as in behat admin-ui pages https://github.com/ibexa/admin-ui/tree/6.0/src/lib/Behat/Page).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored. Extracted the context-menu logic into a shared ContextMenu component in cohesivo-playwright

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is ContextMenu equivalent in Behat? If we plan to build PW automation by converting tests from behat to PW we should stick to existing names.

this.udw = new UniversalDiscoveryWidget(page);
}

async open(): Promise<void> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Navigation between tabs should be done in specific page/component.


async searchInTrash(query: string): Promise<void> {
const url = this.page.url().split('?')[0];
await this.page.goto(`${url}?trash_search[content_name]=${encodeURIComponent(query)}`);

@tomaszszopinski tomaszszopinski Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this code work the same as in behat? Is seems like it bypasses UI interaction by using page.goto()

In behat it looked like:
$this->trashSearch->submitSearchText($searchQuery); $this->trashSearch->confirmSearch();
so, add text to the input -> click search.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to navigation via UI already to make sure every step it's working fine.

@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

this.udw = new UniversalDiscoveryWidget(page);
this.contentActionsMenu = new ContentActionsMenu(page);

this.firstRow = page.locator('.ibexa-table__row').first();

@KamilSznajdrowicz KamilSznajdrowicz Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The empty-state row uses the same class as data rows (empty_table_body_row.html.twig:1: ), so this matches on an empty table too.

Verified on a live instance: right after emptyTrash(), with "Trash is empty…" on screen, .ibexa-table__row still counts 1 — and an extra assertNotEmpty() placed after emptyTrash() passes, i.e. it and assertEmpty() both succeed on the same state.

So in "Trash can be emptied" only the post-condition is tested; the precondition would pass even if "Send to trash" did nothing.

Suggested change
this.firstRow = page.locator('.ibexa-table__row').first();
this.firstRow = page.locator('.ibexa-table__body .ibexa-table__row').filter({hasNot:page.locator('.ibexa-table__empty-table-cell') }).first();

});

test.afterAll(async () => {
await api.deleteContent(trashTestContentId);

@KamilSznajdrowicz KamilSznajdrowicz Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Teardown runs even when beforeAll fails. Hit this locally: createFolder() didn't
return within the 30s hook timeout (slow APP_ENV=dev instance), so
trashTestContentId stayed undefined and got interpolated into the URL:
DELETE /api/ibexa/v2/content/objects/undefined → no route → HTTP 500 → full dev stack
trace in the response, burying the real error ("beforeAll" hook timeout of 30000ms exceeded).

Suggested change
await api.deleteContent(trashTestContentId);
if (api && trashTestContentId) {
await api.deleteContent(trashTestContentId);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants