Files
perauset-e2e-mrfzot2p/e2e/tests/steps/browser/common.steps.ts
2026-07-11 08:34:30 +02:00

142 lines
5.2 KiB
TypeScript

import { Given, When, Then } from '@cucumber/cucumber'
import { expect } from '@playwright/test'
import { BrowserWorld } from '../../support/browser-world.js'
import { browserConfig } from '../../support/browser-config.js'
// ---------------------------------------------------------------------------
// Given
// ---------------------------------------------------------------------------
Given('I am on the login page', async function (this: BrowserWorld) {
await this.navigateTo('/login')
})
Given('I am logged in as {string}', async function (this: BrowserWorld, email: string) {
await this.login(email)
})
// ---------------------------------------------------------------------------
// When — navigation
// ---------------------------------------------------------------------------
When('I navigate to {string}', async function (this: BrowserWorld, path: string) {
await this.navigateTo(path)
})
When('I click {string}', async function (this: BrowserWorld, name: string) {
const page = this.getPage()
// Try button first, then link
const button = page.getByRole('button', { name })
if (await button.isVisible().catch(() => false)) {
await button.click()
} else {
await page.getByRole('link', { name }).click()
}
await page.waitForLoadState('networkidle').catch(() => {})
})
When('I click {string} in the sidebar', async function (this: BrowserWorld, name: string) {
const page = this.getPage()
// Mantine NavLink renders as an <a> WITHOUT href (navigation is via onClick), so
// it has no implicit "link" role — target the label text inside the navbar.
// Some labels are duplicated (e.g. "My Stays" maps to both /my/stays and /stays
// due to the nav.stays == nav.my_stays i18n collision); first() picks the
// personal-section entry, which appears first in DOM order.
const navItem = page
.locator('.mantine-AppShell-navbar')
.getByText(name, { exact: true })
.first()
await navItem.scrollIntoViewIfNeeded()
await navItem.click()
await page.waitForLoadState('networkidle').catch(() => {})
})
When('I click tab {string}', async function (this: BrowserWorld, name: string) {
const page = this.getPage()
await page.getByRole('tab', { name }).click()
await page.waitForLoadState('networkidle').catch(() => {})
})
// ---------------------------------------------------------------------------
// When — form interactions
// ---------------------------------------------------------------------------
When(
'I fill in {string} with {string}',
async function (this: BrowserWorld, label: string, value: string) {
await this.fillField(label, value)
}
)
When(
'I select {string} from {string}',
async function (this: BrowserWorld, value: string, label: string) {
await this.selectOption(label, value)
}
)
When(
'I pick date {string} for {string}',
async function (this: BrowserWorld, day: string, label: string) {
const page = this.getPage()
// Close any open calendar/dropdown first
await page.keyboard.press('Escape')
await page.waitForTimeout(300)
// Mantine DatePickerInput: the label text and button are wrapped in the same container
// Use a CSS selector to find the right DatePickerInput by its label
const wrapper = page.locator(`text="${label}" >> xpath=ancestor::div[contains(@class, "mantine-DatePickerInput-root") or contains(@class, "mantine-InputWrapper-root")][1]`)
const btn = wrapper.locator('button').first()
if (await btn.count() > 0) {
await btn.click({ timeout: 10_000 })
} else {
// Fallback: just try getByLabel
await page.getByLabel(label).click({ timeout: 10_000 })
}
// Wait for calendar popover to appear
await page.waitForTimeout(500)
// Click the day number — try table buttons with matching text
await page.locator('table button').filter({ hasText: new RegExp(`^${day}$`) }).first().click()
// Close the calendar popover
await page.waitForTimeout(300)
}
)
// ---------------------------------------------------------------------------
// Then — assertions
// ---------------------------------------------------------------------------
Then('I should see {string}', async function (this: BrowserWorld, text: string) {
await this.expectText(text)
})
Then('I should not see {string}', async function (this: BrowserWorld, text: string) {
await this.expectNoText(text)
})
Then('I should see heading {string}', async function (this: BrowserWorld, text: string) {
await this.expectHeading(text)
})
Then('I should be on {string}', async function (this: BrowserWorld, path: string) {
const page = this.getPage()
await page.waitForURL((url) => url.pathname.includes(path), { timeout: 10_000 })
expect(page.url()).toContain(path)
})
Then(
'the sidebar should show {string}',
async function (this: BrowserWorld, text: string) {
const page = this.getPage()
const sidebar = page.locator('.mantine-AppShell-navbar')
await expect(sidebar.getByText(text)).toBeVisible({ timeout: 10_000 })
}
)
Then(
'the {string} field should contain {string}',
async function (this: BrowserWorld, label: string, value: string) {
const page = this.getPage()
await expect(page.getByLabel(label)).toHaveValue(value, { timeout: 5_000 })
}
)