# 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/customer/customer-edit.spec.ts >> should edit the language of a business English customer - Location: src/manager/tests/e2e/customer/customer-edit.spec.ts:6:7 # Error details ``` Test timeout of 60000ms exceeded. ``` ``` Error: locator.focus: Test timeout of 60000ms exceeded. Call log: - waiting for locator('.v-dialog:has([data-test-id="customer-edit-general-dialog"]):has(.v-overlay__content:not(.dialog-bottom-transition-enter-active))').locator('.v-input:has(label:has-text("Language"))').locator('.v-field:not(.v-field--loading)').locator('input[type=text]') ``` # Page snapshot ```yaml - generic [ref=e1]: - generic [ref=e6]: - banner [ref=e7]: - generic [ref=e8]: - button [ref=e9] [cursor=pointer]: - generic [ref=e11]: 󰍜 - link [ref=e12] [cursor=pointer]: - /url: / - button "AS" [ref=e14] [cursor=pointer]: - generic [ref=e18]: AS - generic: - text: 󰗊 󰅀 - text: 󰷖 󰍃 - navigation [ref=e19]: - list [ref=e21]: - generic [ref=e23]: Dashboard - link "Tasks" [ref=e24] [cursor=pointer]: - /url: /tasks - img [ref=e27] - generic [ref=e30]: Tasks - link "Facility maps" [ref=e31] [cursor=pointer]: - /url: /facility-map - generic [ref=e33]: 󰧾 - generic [ref=e35]: Facility maps - link "Analytics" [ref=e36] [cursor=pointer]: - /url: /dashboard - generic [ref=e38]: 󱖶 - generic [ref=e40]: Analytics - generic [ref=e42]: Sales - link "Bookings" [ref=e43] [cursor=pointer]: - /url: /bookings - generic [ref=e45]: 󰇡 - generic [ref=e47]: Bookings - link "Customers" [ref=e48] [cursor=pointer]: - /url: /customers - generic [ref=e50]: 󰀏 - generic [ref=e52]: Customers - link "Invoices" [ref=e53] [cursor=pointer]: - /url: /invoices - generic [ref=e55]: 󰷉 - generic [ref=e57]: Invoices - link "Credit notes" [ref=e58] [cursor=pointer]: - /url: /credit-notes - img [ref=e61] - generic [ref=e64]: Credit notes - link "Units" [ref=e65] [cursor=pointer]: - /url: /units - generic [ref=e67]: 󰍀 - generic [ref=e69]: Units - generic [ref=e71]: Site Management - link "Locations" [ref=e72] [cursor=pointer]: - /url: /locations - generic [ref=e74]: 󰟙 - generic [ref=e76]: Locations - link "Unit types" [ref=e77] [cursor=pointer]: - /url: /unit-types - generic [ref=e79]: 󰆧 - generic [ref=e81]: Unit types - link "Protection Plans" [ref=e82] [cursor=pointer]: - /url: /insurances - generic [ref=e84]: 󰳌 - generic [ref=e86]: Protection Plans - link "Deposits" [ref=e87] [cursor=pointer]: - /url: /deposits - generic [ref=e89]: 󱙆 - generic [ref=e91]: Deposits - link "Products" [ref=e92] [cursor=pointer]: - /url: /products - generic [ref=e94]: 󰄑 - generic [ref=e96]: Products - link "Discounts" [ref=e97] [cursor=pointer]: - /url: /discounts - generic [ref=e99]: 󰓼 - generic [ref=e101]: Discounts - generic [ref=e102]: - option "Emails" [ref=e103] [cursor=pointer]: - generic [ref=e105]: 󰻨 - generic [ref=e107]: Emails - generic [ref=e109]: 󰅀 - text: 󱡰 󰁥 - generic [ref=e111]: Admin - link "Integrations" [ref=e112] [cursor=pointer]: - /url: /connected-apps - generic [ref=e114]: 󱘖 - generic [ref=e116]: Integrations - link "User & Roles" [ref=e117] [cursor=pointer]: - /url: /users - generic [ref=e119]: 󰭘 - generic [ref=e121]: User & Roles - generic [ref=e122]: - option "Booking Portal" [ref=e123] [cursor=pointer]: - generic [ref=e125]: 󱃁 - generic [ref=e127]: Booking Portal - generic [ref=e129]: 󰅀 - text: 󰖟 󰟙 - generic [ref=e130]: - option "JaneAI" [ref=e131] [cursor=pointer]: - generic [ref=e133]: 󱙺 - generic [ref=e135]: JaneAI - generic [ref=e137]: 󰅀 - text: 󱜹 - generic [ref=e139]: Feedback - link "Voting Portal" [ref=e140] [cursor=pointer]: - /url: /voting-portal - generic [ref=e142]: 󰔔 - generic [ref=e144]: Voting Portal - main [ref=e145]: - generic [ref=e146]: - generic [ref=e148]: - generic [ref=e149]: - link [ref=e151] [cursor=pointer]: - /url: /customers - generic [ref=e153]: 󰁍 - generic [ref=e156]: - generic [ref=e157]: Customers - generic [ref=e158]: - heading "Daniel Mustermann AG" [level=1] [ref=e159]: - generic [ref=e160]: Daniel Mustermann AG - generic [ref=e163]: Lead - button "Actions" [ref=e165] [cursor=pointer]: - generic [ref=e166]: - text: Actions - generic [ref=e167]: 󱨉 - generic [ref=e168]: - generic [ref=e169]: - generic [ref=e170]: - generic [ref=e171]: - generic [ref=e173]: - heading "General" [level=3] [ref=e175] - link [ref=e177] [cursor=pointer]: - /url: /customers/1009100910/edit/general - generic [ref=e179]: 󰲶 - table [ref=e182]: - rowgroup [ref=e183]: - row "Locations Storeroom Innsbruck City 1030" [ref=e184]: - rowheader "Locations" [ref=e185] - cell "Storeroom Innsbruck City 1030" [ref=e186]: - generic [ref=e189]: - generic [ref=e191]: Storeroom Innsbruck - generic [ref=e193]: City 1030 - row "Customer type Business" [ref=e194]: - rowheader "Customer type" [ref=e195] - cell "Business" [ref=e196]: - generic [ref=e198]: Business - row "Company name Daniel Mustermann AG" [ref=e199]: - rowheader "Company name" [ref=e200] - cell "Daniel Mustermann AG" [ref=e201]: - generic [ref=e203]: Daniel Mustermann AG - row "VAT type -" [ref=e204]: - rowheader "VAT type" [ref=e205] - cell "-" [ref=e206]: - generic [ref=e208]: "-" - row "VAT number AT1009" [ref=e209]: - rowheader "VAT number" [ref=e210] - cell "AT1009" [ref=e211]: - generic [ref=e213]: AT1009 - row "Name Mustermann" [ref=e214]: - rowheader "Name" [ref=e215] - cell "Mustermann" [ref=e216]: - generic [ref=e218]: Mustermann - row "Email daniel.mustermann@gmail.com" [ref=e219]: - rowheader "Email" [ref=e220] - cell "daniel.mustermann@gmail.com" [ref=e221]: - generic [ref=e223]: daniel.mustermann@gmail.com - row "Phone number +55 12341009" [ref=e224]: - rowheader "Phone number" [ref=e225] - cell "+55 12341009" [ref=e226]: - generic [ref=e228]: +55 12341009 - row "Language EN" [ref=e229]: - rowheader "Language" [ref=e230] - cell "EN" [ref=e231]: - generic [ref=e233]: EN - row "Address Mustermann Street 1009 Las Vegas Brazil" [ref=e234]: - rowheader "Address" [ref=e235] - cell "Mustermann Street 1009 Las Vegas Brazil" [ref=e236]: - generic [ref=e238]: Mustermann Street 1009 Las Vegas Brazil - row "Note -" [ref=e239]: - rowheader "Note" [ref=e240] - cell "-" [ref=e241]: - generic [ref=e243]: "-" - generic [ref=e244]: - heading "Optional Consents" [level=3] [ref=e248] - table [ref=e251]: - rowgroup [ref=e252]: - row "Marketing/Newsletter Not agreed" [ref=e253]: - rowheader "Marketing/Newsletter" [ref=e254] - cell "Not agreed" [ref=e255]: - generic [ref=e258]: - checkbox [ref=e266] [cursor=pointer] - generic [ref=e267]: Not agreed - generic [ref=e268]: - generic [ref=e269]: - generic [ref=e271]: - heading "Details" [level=3] [ref=e273] - link [ref=e275] [cursor=pointer]: - /url: /customers/1009100910/edit/details - generic [ref=e277]: 󰲶 - table [ref=e280]: - rowgroup [ref=e281]: - row "Customer number 1009-1009-10" [ref=e282]: - rowheader "Customer number" [ref=e283] - cell "1009-1009-10" [ref=e284]: - generic [ref=e286]: 1009-1009-10 - row "Created at 8 Apr 2026" [ref=e287]: - rowheader "Created at" [ref=e288] - cell "8 Apr 2026" [ref=e289]: - generic [ref=e291]: 8 Apr 2026 - row "Total spent €0.00" [ref=e292]: - rowheader "Total spent" [ref=e293] - cell "€0.00" [ref=e294]: - generic [ref=e296]: €0.00 - row "Total past due amount €0.00" [ref=e297]: - rowheader "Total past due amount" [ref=e298] - cell "€0.00" [ref=e299]: - generic [ref=e301]: €0.00 - row "Payment methods -" [ref=e302]: - rowheader "Payment methods" [ref=e303] - cell "-" [ref=e304]: - generic [ref=e307]: "-" - row "Accounts receivable number -" [ref=e308]: - rowheader "Accounts receivable number" [ref=e309] - cell "-" [ref=e310]: - generic [ref=e312]: "-" - generic [ref=e313]: - heading "Customer credit balance" [level=3] [ref=e317] - generic [ref=e320]: - generic [ref=e321]: - generic "City 1030" [ref=e322] - generic [ref=e323]: - generic [ref=e324]: €0.00 - button [ref=e326] [cursor=pointer]: - generic [ref=e328]: 󰇘 - generic [ref=e329]: - generic "Storeroom Innsbruck" [ref=e330] - generic [ref=e331]: - generic [ref=e332]: €0.00 - button [ref=e334] [cursor=pointer]: - generic [ref=e336]: 󰇘 - generic [ref=e338]: - heading "Requests" [level=3] [ref=e342] - generic [ref=e347]: - generic [ref=e349]: - generic [ref=e355]: - button [ref=e356] [cursor=pointer]: - generic [ref=e358]: 󱒇 - generic: 󰄲 󰄲 󰄱 󰄲 󰄲 󰄲 󰄲 󰄲 󰄲 󰄲 󰄲 - table [ref=e360]: - rowgroup [ref=e361]: - row "Location Unit type Requested at Move-in Booking plan Protection plan One-time products Recurring products Request type Task" [ref=e362]: - columnheader "Location" [ref=e363]: - button "Location" [ref=e364] [cursor=pointer]: - generic [ref=e366]: Location - generic [ref=e369]: 󰁝 - columnheader "Unit type" [ref=e370]: - button "Unit type" [ref=e371] [cursor=pointer]: - generic [ref=e373]: Unit type - generic [ref=e376]: 󰁝 - columnheader "Requested at" [ref=e377]: - button "Requested at" [ref=e378] [cursor=pointer]: - generic [ref=e380]: Requested at - generic [ref=e383]: 󰁝 - columnheader "Move-in" [ref=e384]: - button "Move-in" [ref=e385] [cursor=pointer]: - generic [ref=e387]: Move-in - generic [ref=e390]: 󰁝 - columnheader "Booking plan" [ref=e391]: - button "Booking plan" [ref=e392] [cursor=pointer]: - generic [ref=e394]: Booking plan - generic [ref=e397]: 󰁝 - columnheader "Protection plan" [ref=e398]: - button "Protection plan" [ref=e399] [cursor=pointer]: - generic [ref=e401]: Protection plan - generic [ref=e404]: 󰁝 - columnheader "One-time products" [ref=e405]: - generic [ref=e408]: One-time products - columnheader "Recurring products" [ref=e409]: - generic [ref=e412]: Recurring products - columnheader "Request type" [ref=e413]: - button "Request type" [ref=e414] [cursor=pointer]: - generic [ref=e416]: Request type - generic [ref=e419]: 󰁝 - columnheader "Task" [ref=e420]: - generic [ref=e423]: Task - row [ref=e424]: - columnheader [ref=e425] - rowgroup [ref=e426]: - row "No data yet" [ref=e427]: - cell "No data yet" [ref=e428]: - generic [ref=e429]: No data yet - generic [ref=e432]: - generic [ref=e433]: - generic [ref=e434]: "Items per page:" - combobox [ref=e437] [cursor=pointer]: - generic [ref=e439]: - generic [ref=e441]: "10" - combobox "Items per page:": "10" - generic [ref=e443]: 󰍝 - generic [ref=e444]: 0-0 of 0 - generic [ref=e445]: - button [disabled]: - generic: - generic: 󰘀 - button [disabled]: - generic: - generic: 󰅁 - button [disabled]: - generic: - generic: 󰅂 - button [disabled]: - generic: - generic: 󰘁 - contentinfo [ref=e446]: - generic [ref=e447]: - generic [ref=e448]: - text: Kinnovis GmbH - generic [ref=e449]: 󰗦 - text: "2026" - generic [ref=e450]: v2026.04.03 - generic: - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - dialog: - generic [ref=e453]: - heading "General" [level=3] [ref=e457] - generic [ref=e460]: - group [ref=e461]: - generic [ref=e462]: - generic [ref=e464]: - combobox [ref=e466]: - generic [ref=e468]: - generic [ref=e470]: Business - combobox "Customer type *" [ref=e471] - button [ref=e473] [cursor=pointer]: 󰍝 - generic: - generic: Customer type * - alert [ref=e474] - generic [ref=e477]: - combobox [ref=e479]: - generic [ref=e480]: - generic: Add another location - combobox "Add another location" [ref=e482] - button [ref=e484] [cursor=pointer]: 󰍝 - alert [ref=e485] - generic [ref=e489]: - generic [ref=e491]: - textbox "Company name *" [ref=e493]: Daniel Mustermann AG - generic: - generic: Company name * - alert [ref=e494] - generic [ref=e497]: - generic [ref=e499]: - combobox [ref=e501]: - generic [ref=e502]: - generic: VAT type - combobox "VAT type" [ref=e504] - text: 󰅙 - button [ref=e506] [cursor=pointer]: 󰍝 - alert [ref=e507] - generic [ref=e510]: - generic [ref=e512]: - textbox "VAT number" [ref=e514]: AT1009 - generic: - generic: VAT number - alert [ref=e515] - generic [ref=e517]: - generic [ref=e519]: - generic [ref=e522]: - generic: First name - textbox "First name" [ref=e523] - alert [ref=e524] - generic [ref=e527]: - generic [ref=e529]: - textbox "Last name" [ref=e531]: Mustermann - generic: - generic: Last name - alert [ref=e532] - generic [ref=e535]: - generic [ref=e537]: - textbox "Email address *" [ref=e539]: daniel.mustermann@gmail.com - generic: - generic: Email address * - alert [ref=e540] - generic [ref=e544]: - generic [ref=e548]: - generic [ref=e550]: - button "Brazil" [ref=e551] [cursor=pointer]: - generic [ref=e552]: - img "Brazil" [ref=e553] - generic [ref=e554]: 󰍝 - generic [ref=e555]: "+55" - textbox "Phone number *" [ref=e557]: "12341009" - generic: - generic: Phone number * - alert [ref=e558] - generic [ref=e561]: - combobox [ref=e563]: - generic [ref=e565]: - generic [ref=e567]: English - combobox "Language *" [ref=e568] - button [ref=e570] [cursor=pointer]: 󰍝 - generic: - generic: Language * - alert [ref=e571] - generic [ref=e574]: - generic [ref=e577]: - generic: Note - textbox "Note" [ref=e578] - alert [ref=e579] - group [ref=e581]: - generic [ref=e582]: - generic [ref=e584]: - generic [ref=e586]: - textbox "Street / House Number / Building *" [ref=e588]: Mustermann Street - generic: - generic: Street / House Number / Building * - alert [ref=e589] - generic [ref=e592]: - generic [ref=e594]: - textbox "Postal code *" [ref=e596]: "1009" - generic: - generic: Postal code * - alert [ref=e597] - generic [ref=e600]: - generic [ref=e602]: - textbox "City *" [ref=e604]: Las Vegas - generic: - generic: City * - alert [ref=e605] - generic [ref=e608]: - combobox [ref=e610]: - generic [ref=e612]: - generic [ref=e614]: Brazil - combobox "Country *" [ref=e615] - button [ref=e617] [cursor=pointer]: 󰍝 - generic: - generic: Country * - alert [ref=e618] - contentinfo [ref=e621]: - generic [ref=e623]: - button "Cancel" [ref=e624] [cursor=pointer]: - generic [ref=e625]: Cancel - button "Save" [ref=e626] [cursor=pointer]: - generic [ref=e627]: Save ``` # Test source ```ts 1 | import { OptionMenu } from '@/manager/shared/components/OptionMenu'; 2 | import { BaseComponent } from '@/shared/base/BaseComponent'; 3 | import { sleep } from '@/shared/utils/timeout-utils'; 4 | 5 | export class SingleAutocomplete extends BaseComponent { 6 | private readonly body = this.host.locator('.v-field:not(.v-field--loading)'); 7 | private readonly input = this.body.locator('input[type=text]'); 8 | private readonly closeButton = this.body.locator('.v-field__clearable'); 9 | readonly menu = new OptionMenu(this.input); 10 | 11 | async select(value: string): Promise { 12 | await this.input.focus(); 13 | await this.input.clear(); 14 | await this.input.clear(); 15 | await this.input.fill(value); 16 | await this.menu.select(value); 17 | } 18 | 19 | async fill(value: string): Promise { > 20 | await this.input.focus(); | ^ Error: locator.focus: Test timeout of 60000ms exceeded. 21 | await this.input.clear(); 22 | await this.input.clear(); 23 | // TODO: temporary fix for when the autocomplete is not fully loaded yet e.g. due to currently opening a root dialog 24 | await sleep(75); 25 | await this.input.fill(value); 26 | 27 | if (value) { 28 | const menuBody = await this.menu.getBody(); 29 | await menuBody.waitFor(); 30 | } 31 | 32 | await this.input.press('Enter'); 33 | } 34 | 35 | async fillWithoutEnter(value: string): Promise { 36 | await this.input.focus(); 37 | await this.input.fill(''); 38 | await this.input.fill(value); 39 | } 40 | 41 | async clear(): Promise { 42 | await this.closeButton.hover(); 43 | await this.closeButton.click(); 44 | } 45 | } 46 | ```