# 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-create.spec.ts >> creates a booking with a fixed discount - Location: src/manager/tests/e2e/booking/booking-create.spec.ts:82:7 # Error details ``` Error: expect(locator).toHaveText(expected) failed Locator: getByTestId('app-page').getByTestId('app-page-content').locator('[data-test-id=app-details-card]:has([data-test-id=title]:has-text("General"))').locator('[data-test-id=key-value-item]:has([data-test-id=key]:text-is("Discount"))').getByTestId('value') Expected: "Soft Sale 3DJqWW" Received: "-" Timeout: 30000ms Call log: - Expect "toHaveText" with timeout 30000ms - waiting for getByTestId('app-page').getByTestId('app-page-content').locator('[data-test-id=app-details-card]:has([data-test-id=title]:has-text("General"))').locator('[data-test-id=key-value-item]:has([data-test-id=key]:text-is("Discount"))').getByTestId('value') 34 × locator resolved to … - unexpected value "-" ``` # Page snapshot ```yaml - generic [active] [ref=e1]: - generic [ref=e6]: - navigation [ref=e7]: - generic [ref=e9]: - link [ref=e10] [cursor=pointer]: - /url: / - generic [ref=e11]: - generic [ref=e12]: KINNOVIS - generic [ref=e13]: Manager - button [ref=e15] [cursor=pointer]: - img [ref=e16] - list [ref=e19]: - generic [ref=e22]: Dashboard - link "Tasks" [ref=e23] [cursor=pointer]: - /url: /tasks - img [ref=e26] - generic [ref=e29]: Tasks - link "Facility maps" [ref=e30] [cursor=pointer]: - /url: /facility-map - generic [ref=e32]: 󰧾 - generic [ref=e34]: Facility maps - link "Analytics" [ref=e35] [cursor=pointer]: - /url: /dashboard - generic [ref=e37]: 󱖶 - generic [ref=e39]: Analytics - generic [ref=e41]: Sales - link "Bookings" [ref=e42] [cursor=pointer]: - /url: /bookings - generic [ref=e44]: 󰇡 - generic [ref=e46]: Bookings - link "Customers" [ref=e47] [cursor=pointer]: - /url: /customers - generic [ref=e49]: 󰀏 - generic [ref=e51]: Customers - link "Invoices" [ref=e52] [cursor=pointer]: - /url: /invoices - generic [ref=e54]: 󰷉 - generic [ref=e56]: Invoices - link "Credit notes" [ref=e57] [cursor=pointer]: - /url: /credit-notes - img [ref=e60] - generic [ref=e63]: Credit notes - link "Units" [ref=e64] [cursor=pointer]: - /url: /units - generic [ref=e66]: 󰍀 - generic [ref=e68]: Units - generic [ref=e70]: Site management - link "Locations" [ref=e71] [cursor=pointer]: - /url: /locations - generic [ref=e73]: 󰟙 - generic [ref=e75]: Locations - link "Unit types" [ref=e76] [cursor=pointer]: - /url: /unit-types - generic [ref=e78]: 󰆧 - generic [ref=e80]: Unit types - link "Protection Plans" [ref=e81] [cursor=pointer]: - /url: /insurances - generic [ref=e83]: 󰳌 - generic [ref=e85]: Protection Plans - link "Deposits" [ref=e86] [cursor=pointer]: - /url: /deposits - generic [ref=e88]: 󱙆 - generic [ref=e90]: Deposits - link "Products" [ref=e91] [cursor=pointer]: - /url: /products - generic [ref=e93]: 󰄑 - generic [ref=e95]: Products - link "Discounts" [ref=e96] [cursor=pointer]: - /url: /discounts - generic [ref=e98]: 󰓼 - generic [ref=e100]: Discounts - generic [ref=e101]: - option "Emails" [ref=e102] [cursor=pointer]: - generic [ref=e104]: 󰻨 - generic [ref=e106]: Emails - generic [ref=e110]: 󰅀 - text: 󱡰 󰁥 - generic [ref=e112]: Admin - link "Integrations" [ref=e113] [cursor=pointer]: - /url: /connected-apps - generic [ref=e115]: 󱘖 - generic [ref=e117]: Integrations - link "User & Roles" [ref=e118] [cursor=pointer]: - /url: /users - generic [ref=e120]: 󰭘 - generic [ref=e122]: User & Roles - generic [ref=e123]: - option "Booking Portal" [ref=e124] [cursor=pointer]: - generic [ref=e126]: 󱃁 - generic [ref=e128]: Booking Portal - generic [ref=e132]: 󰅀 - text: 󰖟 󰟙 - generic [ref=e133]: - option "JaneAI" [ref=e134] [cursor=pointer]: - generic [ref=e136]: 󱙺 - generic [ref=e138]: JaneAI - generic [ref=e142]: 󰅀 - text: 󱜹 - generic [ref=e144]: Feedback - link "Voting Portal" [ref=e145] [cursor=pointer]: - /url: /voting-portal - generic [ref=e147]: 󰔔 - generic [ref=e149]: Voting Portal - generic [ref=e152]: - button "AS Andreas Stadler andreas.stadler@storeroom.at" [ref=e155] [cursor=pointer]: - generic [ref=e158]: AS - generic [ref=e159]: - generic [ref=e160]: Andreas Stadler - generic "andreas.stadler@storeroom.at" [ref=e161] - generic [ref=e162]: 󰇙 - generic: - text: 󰗊 󰅀 - text: 󰷖 󰍃 - button [ref=e163] [cursor=pointer]: - generic [ref=e165]: 󰋽 - main [ref=e166]: - generic [ref=e168]: - generic [ref=e170]: - generic [ref=e171]: - link "Bookings" [ref=e172] [cursor=pointer]: - /url: /bookings - generic [ref=e173]: Bookings - generic [ref=e174]: 󰅂 - generic [ref=e175]: Booking 1003-7551-02 - generic [ref=e179]: Active - button "Actions" [ref=e181] [cursor=pointer]: - generic [ref=e182]: - text: Actions - generic [ref=e183]: 󱨉 - generic: 󱕂 - generic [ref=e184]: - generic [ref=e185]: - tablist [ref=e186]: - generic [ref=e188]: - tab "Details" [selected] [ref=e189] [cursor=pointer]: - generic [ref=e190]: Details - tab "Timeline" [ref=e191] [cursor=pointer]: - generic [ref=e192]: Timeline - separator [ref=e193] - generic [ref=e194]: - generic [ref=e195]: - generic [ref=e197]: - heading "General" [level=3] [ref=e201] - table [ref=e204]: - rowgroup [ref=e205]: - row "Customer Alonzo McLaughlin-Pouros" [ref=e206]: - rowheader "Customer" [ref=e207] - cell "Alonzo McLaughlin-Pouros" [ref=e208]: - link "Alonzo McLaughlin-Pouros" [ref=e211] [cursor=pointer]: - /url: /customers/1678801807 - generic [ref=e213]: Alonzo McLaughlin-Pouros - row "Location Vienna South" [ref=e214]: - rowheader "Location" [ref=e215] - cell "Vienna South" [ref=e216]: - link "Vienna South" [ref=e219] [cursor=pointer]: - /url: /locations/5000500050 - generic [ref=e221]: Vienna South - row "Created at 25 Aug 2026" [ref=e222]: - rowheader "Created at" [ref=e223] - cell "25 Aug 2026" [ref=e224]: - generic [ref=e226]: 25 Aug 2026 - row "Move-in 25 Aug 2026" [ref=e227]: - rowheader "Move-in" [ref=e228] - cell "25 Aug 2026" [ref=e229]: - generic [ref=e231]: 25 Aug 2026 - row "Move-out -" [ref=e232]: - rowheader "Move-out" [ref=e233] - cell "-" [ref=e234]: - generic [ref=e236]: "-" - row "Cancelled at -" [ref=e237]: - rowheader "Cancelled at" [ref=e238] - cell "-" [ref=e239]: - generic [ref=e241]: "-" - row "Deposit excl. VAT -" [ref=e242]: - rowheader "Deposit excl. VAT" [ref=e243] - cell "-" [ref=e244]: - generic [ref=e246]: "-" - row "Discount -" [ref=e247]: - rowheader "Discount" [ref=e248] - cell "-" [ref=e249]: - generic [ref=e251]: "-" - row "Contract -" [ref=e252]: - rowheader "Contract" [ref=e253] - cell "-" [ref=e254]: - generic [ref=e256]: "-" - generic [ref=e257]: - generic [ref=e258]: - heading "Billing" [level=3] [ref=e262] - table [ref=e265]: - rowgroup [ref=e266]: - row "Booking Plan Basic (1 week)" [ref=e267]: - rowheader "Booking Plan" [ref=e268] - cell "Basic (1 week)" [ref=e269]: - generic [ref=e271]: Basic (1 week) - row "Current period 25 Aug - 1 Sep 2026" [ref=e272]: - rowheader "Current period" [ref=e273] - cell "25 Aug - 1 Sep 2026" [ref=e274]: - generic [ref=e276]: 25 Aug - 1 Sep 2026 - row "Amount excl. VAT €50.00" [ref=e277]: - rowheader "Amount excl. VAT" [ref=e278] - cell "€50.00" [ref=e279]: - generic [ref=e281]: €50.00 - row "Price adjustment -" [ref=e282]: - rowheader "Price adjustment" [ref=e283] - cell "-" [ref=e284]: - generic [ref=e286]: "-" - row "Next invoice 1 Sep 2026" [ref=e287]: - rowheader "Next invoice" [ref=e288] - cell "1 Sep 2026" [ref=e289]: - generic [ref=e291]: 1 Sep 2026 - row "Payment method -" [ref=e292]: - rowheader "Payment method" [ref=e293] - cell "-" [ref=e294]: - generic [ref=e296]: "-" - row "Payment option Automatic payment through Stripe" [ref=e297]: - rowheader "Payment option" [ref=e298] - cell "Automatic payment through Stripe" [ref=e299]: - generic [ref=e301]: Automatic payment through Stripe - generic [ref=e302]: - generic [ref=e304]: - heading "Notes" [level=3] [ref=e306] - link [ref=e308] [cursor=pointer]: - /url: /bookings/1003755102/edit/notes - generic [ref=e310]: 󰲶 - table [ref=e313]: - rowgroup [ref=e314]: - row "Note -" [ref=e315]: - rowheader "Note" [ref=e316] - cell "-" [ref=e317]: - generic [ref=e319]: "-" - generic [ref=e320]: - heading "Items" [level=3] [ref=e324] - generic [ref=e327]: - generic [ref=e328]: - generic [ref=e329]: - link "Practical Box MTfxkc" [ref=e330] [cursor=pointer]: - /url: /unit-types/1023689487 - generic [ref=e332]: Practical Box MTfxkc - text: / - link "Unit pR2F5F" [ref=e333] [cursor=pointer]: - /url: /units/274794517 - generic [ref=e335]: Unit pR2F5F - generic [ref=e336]: €50.00 - generic [ref=e337]: - generic [ref=e338]: - strong [ref=e339]: Total excl. VAT - strong [ref=e340]: €50.00 - generic [ref=e341]: - generic [ref=e342]: VAT - generic [ref=e343]: €8.50 - generic [ref=e344]: - strong [ref=e345]: Total incl. VAT - strong [ref=e346]: €58.50 - generic [ref=e347]: - generic [ref=e349]: - heading "Invoices" [level=3] [ref=e351] - progressbar [ref=e354]: - img [ref=e355] - generic [ref=e361]: - generic [ref=e363]: - generic [ref=e365]: - button "Select all" [ref=e373] [cursor=pointer]: - generic [ref=e375]: 󰄬 - generic [ref=e376]: Select all - generic [ref=e377]: - button "Mark as paid" [disabled] [ref=e380]: - generic [ref=e382]: 󰗡 - generic [ref=e383]: Mark as paid - button "SEPA XML actions" [disabled] [ref=e386]: - generic [ref=e388]: 󱨉 - generic [ref=e389]: SEPA XML actions - generic: 󰗡 󰅚 󰗖 - generic [ref=e392]: - button [ref=e393] [cursor=pointer]: - img [ref=e396] - generic: 󰄲 󰄲 󰄲 󰄲 󰄲 󰄲 󰄲 󰄲 󰄲 󰄲 - table [ref=e399]: - rowgroup [ref=e400]: - row "Number Status Payment option Due date Unit Discount Total (incl. VAT) Amount due Issue date Actions" [ref=e401]: - columnheader "Number" [ref=e402]: - button "Number" [ref=e403] [cursor=pointer]: - generic [ref=e405]: Number - generic [ref=e408]: 󰁝 - columnheader "Status" [ref=e409]: - button "Status" [ref=e410] [cursor=pointer]: - generic [ref=e412]: Status - generic [ref=e415]: 󰁝 - columnheader "Payment option" [ref=e416]: - button "Payment option" [ref=e417] [cursor=pointer]: - generic [ref=e419]: Payment option - generic [ref=e422]: 󰁝 - columnheader "Due date" [ref=e423]: - button "Due date" [ref=e424] [cursor=pointer]: - generic [ref=e426]: Due date - generic [ref=e429]: 󰁝 - columnheader "Unit" [ref=e430]: - button "Unit" [ref=e431] [cursor=pointer]: - generic [ref=e433]: Unit - generic [ref=e436]: 󰁝 - columnheader "Discount" [ref=e437]: - generic [ref=e440]: Discount - columnheader "Total (incl. VAT)" [ref=e441]: - button "Total (incl. VAT)" [ref=e442] [cursor=pointer]: - generic [ref=e443]: - generic [ref=e444]: Total (incl. VAT) - generic [ref=e446]: 󰋽 - generic [ref=e449]: 󰁝 - columnheader "Amount due" [ref=e450]: - button "Amount due" [ref=e451] [cursor=pointer]: - generic [ref=e453]: Amount due - generic [ref=e456]: 󰁝 - columnheader "Issue date" [ref=e457]: - button "Issue date" [ref=e458] [cursor=pointer]: - generic [ref=e460]: Issue date - generic [ref=e463]: 󰁝 - columnheader "Actions" [ref=e464]: - generic [ref=e467]: Actions - row [ref=e468]: - columnheader [ref=e469] - rowgroup [ref=e470]: - row "No data yet" [ref=e471]: - cell "No data yet" [ref=e472]: - generic [ref=e473]: No data yet - generic [ref=e476]: - generic [ref=e477]: - generic [ref=e478]: "Items per page:" - combobox [ref=e481]: - generic [ref=e483] [cursor=pointer]: - generic [ref=e485]: "10" - combobox "Items per page:": "10" - generic [ref=e487]: 󰍝 - generic [ref=e488]: 0-0 of 0 - generic [ref=e489]: - button [disabled]: - generic: - generic: 󰘀 - button [disabled]: - generic: - generic: 󰅁 - button [disabled]: - generic: - generic: 󰅂 - button [disabled]: - generic: - generic: 󰘁 - generic [ref=e490]: - generic [ref=e492]: - heading "Payments" [level=3] [ref=e494] - progressbar [ref=e497]: - img [ref=e498] - generic [ref=e504]: - table [ref=e508]: - rowgroup [ref=e509]: - row "Total Status Invoice no. Date Payment method" [ref=e510]: - columnheader "Total" [ref=e511]: - button "Total" [ref=e512] [cursor=pointer]: - generic [ref=e514]: Total - generic [ref=e517]: 󰁝 - columnheader "Status" [ref=e518]: - button "Status" [ref=e519] [cursor=pointer]: - generic [ref=e521]: Status - generic [ref=e524]: 󰁝 - columnheader "Invoice no." [ref=e525]: - button "Invoice no." [ref=e526] [cursor=pointer]: - generic [ref=e528]: Invoice no. - generic [ref=e531]: 󰁝 - columnheader "Date" [ref=e532]: - button "Date" [ref=e533] [cursor=pointer]: - generic [ref=e535]: Date - generic [ref=e538]: 󰁝 - columnheader "Payment method" [ref=e539]: - button "Payment method" [ref=e540] [cursor=pointer]: - generic [ref=e542]: Payment method - generic [ref=e545]: 󰁝 - row [ref=e546]: - columnheader [ref=e547] - rowgroup [ref=e548]: - row "No data yet" [ref=e549]: - cell "No data yet" [ref=e550]: - generic [ref=e551]: No data yet - generic [ref=e554]: - generic [ref=e555]: - generic [ref=e556]: "Items per page:" - combobox [ref=e559]: - generic [ref=e561] [cursor=pointer]: - generic [ref=e563]: "10" - combobox "Items per page:": "10" - generic [ref=e565]: 󰍝 - generic [ref=e566]: 0-0 of 0 - generic [ref=e567]: - button [disabled]: - generic: - generic: 󰘀 - button [disabled]: - generic: - generic: 󰅁 - button [disabled]: - generic: - generic: 󰅂 - button [disabled]: - generic: - generic: 󰘁 - generic: - 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 { expect, Locator } from '@playwright/test'; 2 | 3 | export async function expectMultiLocatorToHaveText(locator: Locator, text: string[] | undefined): Promise { 4 | if (!text) { 5 | await expectLocatorToBeEmpty(locator); 6 | return; 7 | } 8 | 9 | const expectedValue = RegExp(text.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')); 10 | 11 | for (const element of await locator.all()) { 12 | await expectSingleLocatorToHaveText(element, expectedValue); 13 | } 14 | } 15 | 16 | export async function expectSingleLocatorToHaveText( 17 | locator: Locator, 18 | text: string | RegExp | undefined 19 | ): Promise { 20 | text > 21 | ? await expect(locator).toHaveText(text, { ignoreCase: true, useInnerText: true }) | ^ Error: expect(locator).toHaveText(expected) failed 22 | : await expectLocatorToBeEmpty(locator); 23 | } 24 | 25 | export async function expectLocatorToBeEmpty(locator: Locator): Promise { 26 | await expect(locator).toHaveText(/^(\s*-?\s*)$/); 27 | } 28 | ```