# Instructions - Following Playwright test failed. - Explain why, be concise, respect Playwright best practices. - Provide a snippet of code with the fix, if possible. # Test info - Name: src/manager/tests/e2e/booking/booking-cancel.spec.ts >> cancels a scheduled booking immediately - Location: src/manager/tests/e2e/booking/booking-cancel.spec.ts:18:7 # Error details ``` Error: expect(locator).toBeVisible() failed Locator: getByTestId('app-page').getByTestId('app-page-header').getByTestId('base-view-header-status-canceled-before-start') Expected: visible Timeout: 30000ms Error: element(s) not found Call log: - Expect "toBeVisible" with timeout 30000ms - waiting for getByTestId('app-page').getByTestId('app-page-header').getByTestId('base-view-header-status-canceled-before-start') ``` ```yaml - navigation: - link: - /url: / - text: KINNOVIS Manager - button: - img - list: - text: Dashboard - link "Tasks": - /url: /tasks - img - text: Tasks - link "Facility maps": - /url: /facility-map - link "Analytics": - /url: /dashboard - text: Sales - link "Bookings": - /url: /bookings - link "Customers": - /url: /customers - link "Invoices": - /url: /invoices - link "Credit notes": - /url: /credit-notes - img - text: Credit notes - link "Units": - /url: /units - text: Site management - link "Locations": - /url: /locations - link "Unit types": - /url: /unit-types - link "Protection Plans": - /url: /insurances - link "Deposits": - /url: /deposits - link "Products": - /url: /products - link "Discounts": - /url: /discounts - option "Emails" - link "Document templates": - /url: /document-templates - text: Admin - link "Integrations": - /url: /connected-apps - link "User & Roles": - /url: /users - option "Booking Portal" - option "JaneAI" - text: Feedback - link "Voting Portal": - /url: /voting-portal - button "AS Andreas Stadler andreas.stadler@storeroom.at" - button - main: - link "Bookings": - /url: /bookings - text: Booking 6586-8432-4114-626 Cancelled - heading "General" [level=3] - table: - rowgroup: - row "Customer Franco Cole": - rowheader "Customer" - cell "Franco Cole": - link "Franco Cole": - /url: /customers/836900462 - row "Location Vienna South": - rowheader "Location" - cell "Vienna South": - link "Vienna South": - /url: /locations/5000500050 - row "Created at 24 Aug 2026": - rowheader "Created at" - cell "24 Aug 2026" - row "Move-in 8 Sep 2026": - rowheader "Move-in" - cell "8 Sep 2026" - row "Move-out 24 Aug 2026": - rowheader "Move-out" - cell "24 Aug 2026" - row "Cancelled at 24 Aug 2026, 04:18": - rowheader "Cancelled at" - cell "24 Aug 2026, 04:18" - row "Deposit excl. VAT -": - rowheader "Deposit excl. VAT" - cell "-" - row "Discount -": - rowheader "Discount" - cell "-" - row "Contract -": - rowheader "Contract" - cell "-" - heading "Billing" [level=3] - table: - rowgroup: - row "Booking Plan Basic (1 month)": - rowheader "Booking Plan" - cell "Basic (1 month)" - row "Current period 8 Sep - 8 Oct 2026": - rowheader "Current period" - cell "8 Sep - 8 Oct 2026" - row "Amount excl. VAT €50.00": - rowheader "Amount excl. VAT" - cell "€50.00" - row "Price adjustment -": - rowheader "Price adjustment" - cell "-" - row "Next invoice 8 Sep 2026": - rowheader "Next invoice" - cell "8 Sep 2026" - row "Payment method -": - rowheader "Payment method" - cell "-" - row "Payment option Automatic payment through Stripe": - rowheader "Payment option" - cell "Automatic payment through Stripe" - heading "Notes" [level=3] - link: - /url: /bookings/658684324114626/edit/notes - table: - rowgroup: - row "Note -": - rowheader "Note" - cell "-" - heading "Items" [level=3] - link "Refined Box VQv7Nn": - /url: /unit-types/3936455 - text: / - link "Unit 8GKVVW": - /url: /units/1995664296 - text: €50.00 - strong: Total excl. VAT - strong: €50.00 - text: VAT €8.50 - strong: Total incl. VAT - strong: €58.50 - heading "Invoices" [level=3] - progressbar: - img - button "Select all" - button "Mark as paid" [disabled] - button "SEPA XML actions" [disabled] - button: - img - table: - rowgroup: - row "Number Status Payment option Due date Unit Discount Total (incl. VAT) Amount due Issue date Actions": - columnheader "Number": - button "Number" - columnheader "Status": - button "Status" - columnheader "Payment option": - button "Payment option" - columnheader "Due date": - button "Due date" - columnheader "Unit": - button "Unit" - columnheader "Discount" - columnheader "Total (incl. VAT)": - button "Total (incl. VAT)" - columnheader "Amount due": - button "Amount due" - columnheader "Issue date": - button "Issue date" - columnheader "Actions" - row: - columnheader - rowgroup: - row "No data yet": - cell "No data yet" - text: "Items per page:" - combobox: - text: "10" - combobox "Items per page:": "10" - text: 0-0 of 0 - button [disabled] - button [disabled] - button [disabled] - button [disabled] - heading "Payments" [level=3] - progressbar: - img - table: - rowgroup: - row "Total Status Invoice no. Date Payment method": - columnheader "Total": - button "Total" - columnheader "Status": - button "Status" - columnheader "Invoice no.": - button "Invoice no." - columnheader "Date": - button "Date" - columnheader "Payment method": - button "Payment method" - row: - columnheader - rowgroup: - row "No data yet": - cell "No data yet" - text: "Items per page:" - combobox: - text: "10" - combobox "Items per page:": "10" - text: 0-0 of 0 - button [disabled] - button [disabled] - button [disabled] - button [disabled] - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip ``` # Test source ```ts 1 | import { getBookingCancelData, getBookingCreateData } from '@/manager/modules/booking/booking-factories'; 2 | import { test } from '@/manager/modules/booking/booking-fixtures'; 3 | import { toBookingCancellationDetails } from '@/manager/modules/booking/booking-mappers'; 4 | import { bookingCancelHappyTestCases } from '@/manager/modules/booking/test-cases/booking-cancel-happy-test-cases'; 5 | import { getCustomerCreateData } from '@/manager/modules/customer/customer-factories'; 6 | import { 7 | expectDataTableChipsColumnToHaveText, 8 | expectDataTableTextColumnToHaveText, 9 | } from '@/manager/modules/ui/data-table/data-table-assertions'; 10 | import { bookingTableColumnTestIds } from '@/manager/modules/ui/data-table/data/data-table-column-test-ids'; 11 | import { getUnitCreateData } from '@/manager/modules/unit/unit-factories'; 12 | import { getUnitTypeCreateData } from '@/manager/modules/unit-type/unit-type-factories'; 13 | import { expectSingleLocatorToHaveText } from '@/manager/shared/utils/expect-utils'; 14 | import { formatId } from '@/shared/utils/formatters'; 15 | import { expect } from '@/shared/utils/matchers'; 16 | 17 | for (const tc of bookingCancelHappyTestCases) { 18 | test(tc.description, async ({ createCustomer, createUnitType, createUnit, createBooking, bookingDetailsPage }) => { 19 | const [customer, unitType] = await Promise.all([ 20 | createCustomer(getCustomerCreateData()), 21 | createUnitType(getUnitTypeCreateData()), 22 | ]); 23 | const unit = await createUnit(getUnitCreateData(unitType)); 24 | const bookingData = getBookingCreateData({ customer, unitType, unit }, tc.initialOptions); 25 | const cancellation = getBookingCancelData(tc.cancellation); 26 | 27 | const { oldStatus, newStatus, moveOutDate } = toBookingCancellationDetails(bookingData, cancellation); 28 | 29 | const booking = await createBooking(bookingData); 30 | 31 | await bookingDetailsPage.goto(booking.id); 32 | 33 | const detailsPageAfterCancel = await test.step('cancel booking', async () => { 34 | const cancelDialog = await bookingDetailsPage.openBookingCancelDialog(); 35 | return cancelDialog.cancel(cancellation); 36 | }); 37 | 38 | await test.step('verify booking after cancel on details page', async () => { 39 | await expectSingleLocatorToHaveText(detailsPageAfterCancel.baseStatus, newStatus); 40 | 41 | if (oldStatus === 'scheduled') { > 42 | await expect(detailsPageAfterCancel.canceledBeforeStartStatus).toBeVisible(); | ^ Error: expect(locator).toBeVisible() failed 43 | } else if (oldStatus === 'active' && newStatus !== 'cancelled') { 44 | await expectSingleLocatorToHaveText(detailsPageAfterCancel.cancelsOnStatus, `ends on ${moveOutDate}`); 45 | } 46 | 47 | await expectSingleLocatorToHaveText(detailsPageAfterCancel.generalCard.moveOut, moveOutDate); 48 | }); 49 | 50 | await test.step('verify booking after cancel on list page', async () => { 51 | const listPage = await detailsPageAfterCancel.returnToBookingListPage(); 52 | 53 | await listPage.searchTextField.fill(formatId(booking.id)); 54 | 55 | await expect(listPage.dataTable.getRows()).toHaveCount(1); 56 | await expectDataTableChipsColumnToHaveText( 57 | listPage.dataTable, 58 | bookingTableColumnTestIds.status, 59 | newStatus === 'cancelled' ? 'cancelled' : [newStatus, ''] 60 | ); 61 | await expectDataTableTextColumnToHaveText(listPage.dataTable, bookingTableColumnTestIds.moveOut, moveOutDate); 62 | }); 63 | }); 64 | } 65 | ```