# 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: "Elegant Sale 0bBZcH"
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')
33 × 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 1620-1827-69
- 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 Diana Murazik" [ref=e206]:
- rowheader "Customer" [ref=e207]
- cell "Diana Murazik" [ref=e208]:
- link "Diana Murazik" [ref=e211] [cursor=pointer]:
- /url: /customers/71593151
- generic [ref=e213]: Diana Murazik
- 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 20 Aug 2026" [ref=e222]:
- rowheader "Created at" [ref=e223]
- cell "20 Aug 2026" [ref=e224]:
- generic [ref=e226]: 20 Aug 2026
- row "Move-in 20 Aug 2026" [ref=e227]:
- rowheader "Move-in" [ref=e228]
- cell "20 Aug 2026" [ref=e229]:
- generic [ref=e231]: 20 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 20 - 27 Aug 2026" [ref=e272]:
- rowheader "Current period" [ref=e273]
- cell "20 - 27 Aug 2026" [ref=e274]:
- generic [ref=e276]: 20 - 27 Aug 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 27 Aug 2026" [ref=e287]:
- rowheader "Next invoice" [ref=e288]
- cell "27 Aug 2026" [ref=e289]:
- generic [ref=e291]: 27 Aug 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/1620182769/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 "Intelligent Box XribNb" [ref=e330] [cursor=pointer]:
- /url: /unit-types/917189854
- generic [ref=e332]: Intelligent Box XribNb
- text: /
- link "Unit Saw588" [ref=e333] [cursor=pointer]:
- /url: /units/274794517
- generic [ref=e335]: Unit Saw588
- 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 |
```