# 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/invoice/invoice-charge-customer.spec.ts >> charges the customer of an open invoice with bacs - Location: src/manager/tests/e2e/invoice/invoice-charge-customer.spec.ts:22:7 # Error details ``` Test timeout of 120000ms exceeded. ``` ``` Error: locator.waitFor: Test timeout of 120000ms exceeded. Call log: - waiting for locator('.v-dialog:has([data-test-id="invoice-charge-customer-dialog"]):has(.v-overlay__content:not(.dialog-bottom-transition-enter-active))').getByTestId('segmented-card').filter({ hasText: 'Bacs Direct Debit •••• 2345' }) to be visible ``` # Page snapshot ```yaml - generic [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] - list [ref=e19]: - generic [ref=e21]: Dashboard - link "Tasks" [ref=e23] [cursor=pointer]: - /url: /tasks - link "Facility maps" [ref=e30] [cursor=pointer]: - /url: /facility-map - generic [ref=e31]: 󰧾 - link "Analytics" [ref=e35] [cursor=pointer]: - /url: /dashboard - generic [ref=e36]: 󱖶 - generic [ref=e40]: Sales - link "Bookings" [ref=e42] [cursor=pointer]: - /url: /bookings - generic [ref=e43]: 󰇡 - link "Customers" [ref=e47] [cursor=pointer]: - /url: /customers - generic [ref=e48]: 󰀏 - link "Invoices" [ref=e52] [cursor=pointer]: - /url: /invoices - generic [ref=e53]: 󰷉 - link "Credit notes" [ref=e57] [cursor=pointer]: - /url: /credit-notes - link "Units" [ref=e64] [cursor=pointer]: - /url: /units - generic [ref=e65]: 󰍀 - generic [ref=e69]: Site management - link "Locations" [ref=e71] [cursor=pointer]: - /url: /locations - generic [ref=e72]: 󰟙 - link "Unit types" [ref=e76] [cursor=pointer]: - /url: /unit-types - generic [ref=e77]: 󰆧 - link "Protection Plans" [ref=e81] [cursor=pointer]: - /url: /insurances - generic [ref=e82]: 󰳌 - link "Deposits" [ref=e86] [cursor=pointer]: - /url: /deposits - generic [ref=e87]: 󱙆 - link "Products" [ref=e91] [cursor=pointer]: - /url: /products - generic [ref=e92]: 󰄑 - link "Discounts" [ref=e96] [cursor=pointer]: - /url: /discounts - generic [ref=e97]: 󰓼 - generic [ref=e101]: - option "Emails" [ref=e102] [cursor=pointer]: - generic [ref=e103]: 󰻨 - generic [ref=e107]: 󰅀 - text: 󱡰 󰁥 - generic [ref=e111]: Admin - link "Integrations" [ref=e113] [cursor=pointer]: - /url: /connected-apps - generic [ref=e114]: 󱘖 - link "User & Roles" [ref=e118] [cursor=pointer]: - /url: /users - generic [ref=e119]: 󰭘 - generic [ref=e123]: - option "Booking Portal" [ref=e124] [cursor=pointer]: - generic [ref=e125]: 󱃁 - generic [ref=e129]: 󰅀 - text: 󰖟 󰟙 - link "Public API" [ref=e133] [cursor=pointer]: - /url: /public-api - generic [ref=e134]: 󱂛 - generic [ref=e138]: Feedback - link "Voting Portal" [ref=e140] [cursor=pointer]: - /url: /voting-portal - generic [ref=e141]: 󰔔 - generic [ref=e147]: - button "AS Andreas Stadler andreas.stadler@storeroom.at" [ref=e150] [cursor=pointer]: - generic [ref=e151]: AS - generic [ref=e154]: - generic [ref=e155]: Andreas Stadler - generic "andreas.stadler@storeroom.at" [ref=e156] - generic [ref=e157]: 󰇙 - generic: 󰗊 󰅀 󰷖 󰍃 - button [ref=e158] [cursor=pointer]: - generic [ref=e159]: 󰋽 - main [ref=e161]: - generic [ref=e163]: - generic [ref=e165]: - generic [ref=e166]: - link "Invoices" [ref=e167] [cursor=pointer]: - /url: /invoices - generic [ref=e169]: 󰅂 - generic [ref=e170]: 8FF84C64-17605 - generic [ref=e171]: Open - button [ref=e177] [cursor=pointer]: - generic [ref=e178]: - text: Actions - generic [ref=e179]: 󱨉 - generic: 󰇚 󱴆 󱳱 󱤒 󰆛 󰗡 󱈎 - generic [ref=e181]: - generic [ref=e183]: - heading "General" [level=3] [ref=e187] - table [ref=e190]: - rowgroup [ref=e191]: - row [ref=e192]: - rowheader "Location" [ref=e193] - cell "Selfstorage UK" [ref=e194] - row [ref=e197]: - rowheader "Issue date" [ref=e198] - cell "17 Oct 2026" [ref=e199] - row [ref=e202]: - rowheader "Due date" [ref=e203] - cell "19 Oct 2026" [ref=e204] - row [ref=e207]: - rowheader "Total" [ref=e208] - cell "€212.40" [ref=e209] - row [ref=e212]: - rowheader "Amount due" [ref=e213] - cell "€212.40" [ref=e214] - row [ref=e217]: - rowheader "Period" [ref=e218] - cell "-" [ref=e219] - row [ref=e222]: - rowheader "Payment option" [ref=e223] - cell "Manual" [ref=e224] - row [ref=e227]: - rowheader "Payment link" [ref=e228] - cell "https://invoice.stripe.com/i/acct_1Pq7nLLXIL7CkUTi/test_YWNjdF8xUHE3bkxMWElMN0NrVVRpLF9WU0h2YXNBN2NjZlM2S2pDY3NoczJLeXF4eHBpbEs3LDE4Mjc0NDg5Ng02007mW4WNZB?s=ap" [ref=e229]: - generic [ref=e230]: - generic [ref=e231]: https://invoice.stripe.com/i/acct_1Pq7nLLXIL7CkUTi/test_YWNjdF8xUHE3bkxMWElMN0NrVVRpLF9WU0h2YXNBN2NjZlM2S2pDY3NoczJLeXF4eHBpbEs3LDE4Mjc0NDg5Ng02007mW4WNZB?s=ap - button [ref=e235] [cursor=pointer]: - generic [ref=e236]: 󰏌 - generic [ref=e239]: - heading "Customer details" [level=3] [ref=e243] - table [ref=e246]: - rowgroup [ref=e247]: - row [ref=e248]: - rowheader "Name" [ref=e249] - cell [ref=e250]: - link "Kiera Flatley" [ref=e253] [cursor=pointer]: - /url: /customers/1365598635 - row [ref=e256]: - rowheader "Customer type" [ref=e257] - cell "Private" [ref=e258] - row [ref=e261]: - rowheader "Email" [ref=e262] - cell [ref=e263]: - link "joseph38@yahoo.com" [ref=e266] [cursor=pointer]: - /url: mailto:joseph38@yahoo.com - row [ref=e267]: - rowheader "Phone number" [ref=e268] - cell "+43 885 1460547" [ref=e269] - row [ref=e272]: - rowheader "Billing address" [ref=e273] - cell "Darby Station 02823-7838 Rapid City Austria" [ref=e274] - generic [ref=e278]: - heading "Payments" [level=3] [ref=e282] - generic [ref=e286]: - table [ref=e290]: - rowgroup [ref=e291]: - row [ref=e292]: - columnheader [ref=e293]: - button "Total" [ref=e294] [cursor=pointer]: - generic [ref=e297]: 󰁝 - columnheader [ref=e300]: - button "Status" [ref=e301] [cursor=pointer]: - generic [ref=e304]: 󰁝 - columnheader [ref=e307]: - button "Invoice no." [ref=e308] [cursor=pointer]: - generic [ref=e311]: 󰁝 - columnheader [ref=e314]: - button "Date" [ref=e315] [cursor=pointer]: - generic [ref=e318]: 󰁝 - columnheader [ref=e321]: - button "Payment method" [ref=e322] [cursor=pointer]: - generic [ref=e325]: 󰁝 - row [ref=e328]: - columnheader [ref=e329] - rowgroup [ref=e330]: - row [ref=e331]: - cell "€212.40" [ref=e332] - cell "Not paid yet" [ref=e334] - cell "8FF84C64-17605" [ref=e339] - cell "17 Oct 2026" [ref=e341] - cell [ref=e343] - generic [ref=e346]: - generic [ref=e347]: - generic [ref=e348]: "Items per page:" - combobox [ref=e351]: - generic [ref=e353] [cursor=pointer]: - generic [ref=e354]: "10" - combobox "Items per page:": "10" - generic [ref=e356]: 󰍝 - generic [ref=e358]: 1-1 of 1 - generic [ref=e359]: - button [disabled]: - generic: 󰘀 - button [disabled]: - generic: 󰅁 - button [disabled]: - generic: 󰅂 - button [disabled]: - generic: 󰘁 - generic [ref=e362]: - heading "Late Fees" [level=3] [ref=e367] - generic [ref=e371]: - table [ref=e375]: - rowgroup [ref=e376]: - row [ref=e377]: - columnheader "Invoice no." [ref=e378] - columnheader "Invoice note" [ref=e382] - columnheader "Created by" [ref=e386] - columnheader "Total" [ref=e390] - columnheader "Status" [ref=e394] - columnheader "Issue date" [ref=e398] - columnheader "Due date" [ref=e402] - row [ref=e406]: - columnheader [ref=e407] - rowgroup [ref=e408]: - row [ref=e409]: - cell "No data yet" [ref=e410] - generic [ref=e414]: - generic [ref=e415]: - generic [ref=e416]: "Items per page:" - combobox [ref=e419]: - generic [ref=e421] [cursor=pointer]: - generic [ref=e422]: "10" - combobox "Items per page:": "10" - generic [ref=e424]: 󰍝 - generic [ref=e426]: 0-0 of 0 - generic [ref=e427]: - button [disabled]: - generic: 󰘀 - button [disabled]: - generic: 󰅁 - button [disabled]: - generic: 󰅂 - button [disabled]: - 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 - dialog: - generic [ref=e430]: - heading "Charge customer for invoice" [level=3] [ref=e434] - generic [ref=e438]: - generic [ref=e439]: - generic [ref=e440]: Amount due - generic [ref=e441]: €212.40 - generic [ref=e443]: - generic [ref=e444]: Existing payment method - button "Add payment method" [ref=e448] [cursor=pointer] - contentinfo [ref=e451]: - generic [ref=e454]: - button "Cancel" [ref=e455] [cursor=pointer] - button "Charge" [ref=e457] [cursor=pointer] - dialog: - generic [ref=e463]: - button [ref=e465] [cursor=pointer]: - generic [ref=e466]: 󰅖 - iframe [active] [ref=e469]: - generic [ref=f2e7]: - generic [ref=f2e8]: Test Mode - generic [ref=f2e11]: - main [ref=f2e12]: - generic [ref=f2e16]: - generic [ref=f2e24]: - generic [ref=f2e25]: Email - generic [ref=f2e26]: joseph38@yahoo.com - generic [ref=f2e36]: - generic [ref=f2e37]: Bacs Direct Debit - generic [ref=f2e57]: - generic [ref=f2e58]: - heading "Confirm your Direct Debit details" [level=3] [ref=f2e59] - button "Modify" [ref=f2e60] [cursor=pointer] - generic [ref=f2e63]: - generic [ref=f2e64]: - term [ref=f2e66]: - generic [ref=f2e67]: Email - definition [ref=f2e69]: - generic [ref=f2e70]: joseph38@yahoo.com - generic [ref=f2e71]: - term [ref=f2e73]: - generic [ref=f2e74]: Name on account - definition [ref=f2e76]: - generic [ref=f2e77]: Ghostface - generic [ref=f2e78]: - term [ref=f2e80]: - generic [ref=f2e81]: Sort code - definition [ref=f2e83]: - generic [ref=f2e84]: 10-88-00 - generic [ref=f2e85]: - term [ref=f2e87]: - generic [ref=f2e88]: Account number - definition [ref=f2e90]: - generic [ref=f2e91]: "00012345" - generic [ref=f2e92]: - paragraph [ref=f2e93]: An email will be sent to joseph38@yahoo.com within three business days to confirm the setup of this debit instruction. - paragraph [ref=f2e94]: Additionally, you will receive two days advance notice via email of any amount to be debited under this instruction. Payments will show as 'Stripe' on your bank statement. - generic [ref=f2e96]: - generic [ref=f2e99]: - checkbox "I hereby declare that I am acting on behalf of my customer \"Kiera Flatley\" who is the account holder and the only person required to authorise debits from this account. By saving the payment information of \"Kiera Flatley\", \"Kiera Flatley\" allows Storeroom to charge \"Kiera Flatley\" for future payments in accordance with their terms." [checked] [ref=f2e101] [cursor=pointer] - paragraph [ref=f2e105] [cursor=pointer]: I hereby declare that I am acting on behalf of my customer "Kiera Flatley" who is the account holder and the only person required to authorise debits from this account. By saving the payment information of "Kiera Flatley", "Kiera Flatley" allows Storeroom to charge "Kiera Flatley" for future payments in accordance with their terms. - generic [ref=f2e108]: - generic [ref=f2e109]: - paragraph [ref=f2e110]: - text: Your payments are protected by the - link "Direct Debit Guarantee" [ref=f2e111] [cursor=pointer]: - /url: https://stripe.com/legal/bacs-direct-debit-guarantee - text: . - img "Bacs Debit Logo" [ref=f2e112] - paragraph [ref=f2e113]: - text: Stripe, 9th Floor, 107 CheapsideLondon EC2V 6DN - link "support@stripe.com" [ref=f2e114] [cursor=pointer]: - /url: mailto:support@stripe.com - button "Confirm" [ref=f2e117] [cursor=pointer]: - generic: - generic [ref=f2e119]: Confirm - generic [ref=f2e120]: Processing - contentinfo [ref=f2e133]: - link [ref=f2e135] [cursor=pointer]: - /url: https://stripe.com - generic [ref=f2e136]: - text: Powered by - img "Stripe" [ref=f2e138] - link "Terms" [ref=f2e140] [cursor=pointer]: - /url: https://stripe.com/legal/end-users - link "Privacy" [ref=f2e141] [cursor=pointer]: - /url: https://stripe.com/privacy - tooltip ``` # Test source ```ts 1 | import { PaymentMethodAddDialog } from '@/manager/modules/ui/payment-method/views/PaymentMethodAddDialog'; 2 | import { getSegmentedCardHost } from '@/manager/shared/utils/locator-utils'; 3 | import { BaseComponent } from '@/shared/base/BaseComponent'; 4 | import { PaymentMethod } from '@/shared/modules/payment-method/payment-method-types'; 5 | import { formatPaymentMethodMaskedNumber } from '@/shared/utils/formatters'; 6 | 7 | export class PaymentMethodAddForm extends BaseComponent { 8 | readonly loading = this.host.getByTestId('payment-methods-dropdown-loading'); 9 | readonly addButton = this.host.getByTestId('add-payment-method-button'); 10 | 11 | async select(method: PaymentMethod): Promise { 12 | const segmentedCard = getSegmentedCardHost(this.host, this.formatSegmentedCardLabel(method)); 13 | 14 | await this.loading.waitFor({ state: 'hidden' }); 15 | 16 | if (await segmentedCard.isHidden()) { 17 | await this.add(method); 18 | } else { 19 | await segmentedCard.click(); 20 | } 21 | } 22 | 23 | async add(method: PaymentMethod): Promise { 24 | const segmentedCard = getSegmentedCardHost(this.host, this.formatSegmentedCardLabel(method)); 25 | 26 | await this.loading.waitFor({ state: 'hidden' }); 27 | 28 | const dialog = await this.openAddDialog(); > 29 | await Promise.all([segmentedCard.waitFor(), dialog.add(method)]); | ^ Error: locator.waitFor: Test timeout of 120000ms exceeded. 30 | } 31 | 32 | private async openAddDialog(): Promise { 33 | await this.addButton.click(); 34 | return new PaymentMethodAddDialog(this.host.page()); 35 | } 36 | 37 | private formatSegmentedCardLabel(method: PaymentMethod): string { 38 | let label: string; 39 | 40 | switch (method.kind) { 41 | case 'sepa': 42 | label = 'IBAN'; 43 | break; 44 | case 'card': 45 | label = 'Card'; 46 | break; 47 | case 'bacs': 48 | label = 'Bacs Direct Debit'; 49 | break; 50 | } 51 | 52 | return `${label} ${formatPaymentMethodMaskedNumber(method)}`; 53 | } 54 | } 55 | ```