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 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 }) } )