# 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/unit/unit-edit.spec.ts >> should not change the status of a rented unit - Location: src/manager/tests/e2e/unit/unit-edit.spec.ts:42:7 # Error details ``` Test timeout of 60000ms exceeded. ``` ``` Error: locator.click: Test timeout of 60000ms exceeded. Call log: - waiting for getByTestId('app-page').getByTestId('app-page-header').getByTestId('unit-action-button') ``` # Page snapshot ```yaml - generic [active] [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=e149]: - link [ref=e151] [cursor=pointer]: - /url: /units - generic [ref=e153]: 󰁍 - generic [ref=e156]: - generic [ref=e157]: Units - generic [ref=e158]: - 'heading "Storage Cube #17" [level=1] [ref=e159]': - generic [ref=e160]: "Storage Cube #17" - generic [ref=e163]: Rented - generic [ref=e165]: - generic [ref=e166]: - generic [ref=e168]: - generic [ref=e170]: - heading "General" [level=3] [ref=e172] - link [ref=e174] [cursor=pointer]: - /url: /units/1798179816/edit/general - generic [ref=e176]: 󰲶 - table [ref=e179]: - rowgroup [ref=e180]: - 'row "Name Storage Cube #17" [ref=e181]': - rowheader "Name" [ref=e182] - 'cell "Storage Cube #17" [ref=e183]': - 'generic "Storage Cube #17" [ref=e185]' - row "Location Vienna South" [ref=e186]: - rowheader "Location" [ref=e187] - cell "Vienna South" [ref=e188]: - generic "Vienna South" [ref=e190] - row "Unit type Portable Storage Container" [ref=e191]: - rowheader "Unit type" [ref=e192] - cell "Portable Storage Container" [ref=e193]: - link "Portable Storage Container" [ref=e196] [cursor=pointer]: - /url: /unit-types/1798179800 - generic [ref=e198]: Portable Storage Container - row "Note -" [ref=e199]: - rowheader "Note" [ref=e200] - cell "-" [ref=e201]: - generic [ref=e203]: "-" - generic [ref=e206]: - generic [ref=e208]: - heading "Measurements" [level=3] [ref=e210] - link [ref=e212] [cursor=pointer]: - /url: /units/1798179816/edit/measurements - generic [ref=e214]: 󰲶 - table [ref=e217]: - rowgroup [ref=e218]: - row "Area 10 m²" [ref=e219]: - rowheader "Area" [ref=e220] - cell "10 m²" [ref=e221]: - generic "10 m²" [ref=e223] - row "Ceiling height 5 cm" [ref=e224]: - rowheader "Ceiling height" [ref=e225] - cell "5 cm" [ref=e226]: - generic "5 cm" [ref=e228] - row "Width 100 cm" [ref=e229]: - rowheader "Width" [ref=e230] - cell "100 cm" [ref=e231]: - generic "100 cm" [ref=e233] - row "Depth 100 cm" [ref=e234]: - rowheader "Depth" [ref=e235] - cell "100 cm" [ref=e236]: - generic "100 cm" [ref=e238] - row "Volume 5 m³" [ref=e239]: - rowheader "Volume" [ref=e240] - cell "5 m³" [ref=e241]: - generic "5 m³" [ref=e243] - row "Door type Single swing door" [ref=e244]: - rowheader "Door type" [ref=e245] - cell "Single swing door" [ref=e246]: - generic "Single swing door" [ref=e248] - row "Door width 5 cm" [ref=e249]: - rowheader "Door width" [ref=e250] - cell "5 cm" [ref=e251]: - generic "5 cm" [ref=e253] - row "Door height 5 cm" [ref=e254]: - rowheader "Door height" [ref=e255] - cell "5 cm" [ref=e256]: - generic "5 cm" [ref=e258] - row "Floor 1" [ref=e259]: - rowheader "Floor" [ref=e260] - cell "1" [ref=e261]: - generic "1" [ref=e263] - generic [ref=e266]: - heading "Bookings" [level=3] [ref=e270] - generic [ref=e275]: - table [ref=e279]: - rowgroup [ref=e280]: - row "Booking number Location Status Customer Unit Booking plan Next invoice Created at Move-in Move-out" [ref=e281]: - columnheader "Booking number" [ref=e282]: - button "Booking number" [ref=e283] [cursor=pointer]: - generic [ref=e285]: Booking number - generic [ref=e288]: 󰁝 - columnheader "Location" [ref=e289]: - generic [ref=e292]: Location - columnheader "Status" [ref=e293]: - button "Status" [ref=e294] [cursor=pointer]: - generic [ref=e296]: Status - generic [ref=e299]: 󰁝 - columnheader "Customer" [ref=e300]: - button "Customer" [ref=e301] [cursor=pointer]: - generic [ref=e303]: Customer - generic [ref=e306]: 󰁝 - columnheader "Unit" [ref=e307]: - button "Unit" [ref=e308] [cursor=pointer]: - generic [ref=e310]: Unit - generic [ref=e313]: 󰁝 - columnheader "Booking plan" [ref=e314]: - button "Booking plan" [ref=e315] [cursor=pointer]: - generic [ref=e317]: Booking plan - generic [ref=e320]: 󰁝 - columnheader "Next invoice" [ref=e321]: - button "Next invoice" [ref=e322] [cursor=pointer]: - generic [ref=e324]: Next invoice - generic [ref=e327]: 󰁝 - columnheader "Created at" [ref=e328]: - button "Created at" [ref=e329] [cursor=pointer]: - generic [ref=e331]: Created at - generic [ref=e334]: 󰁝 - columnheader "Move-in" [ref=e335]: - button "Move-in" [ref=e336] [cursor=pointer]: - generic [ref=e338]: Move-in - generic [ref=e341]: 󰁝 - columnheader "Move-out" [ref=e342]: - button "Move-out" [ref=e343] [cursor=pointer]: - generic [ref=e345]: Move-out - generic [ref=e348]: 󰁝 - row [ref=e349]: - columnheader [ref=e350] - rowgroup [ref=e351]: - row "No data yet" [ref=e352]: - cell "No data yet" [ref=e353]: - generic [ref=e354]: No data yet - generic [ref=e357]: - generic [ref=e358]: - generic [ref=e359]: "Items per page:" - combobox [ref=e362] [cursor=pointer]: - generic [ref=e364]: - generic [ref=e366]: "10" - combobox "Items per page:": "10" - generic [ref=e368]: 󰍝 - generic [ref=e369]: 0-0 of 0 - generic [ref=e370]: - button [disabled]: - generic: - generic: 󰘀 - button [disabled]: - generic: - generic: 󰅁 - button [disabled]: - generic: - generic: 󰅂 - button [disabled]: - generic: - generic: 󰘁 - contentinfo [ref=e371]: - generic [ref=e372]: - generic [ref=e373]: - text: Kinnovis GmbH - generic [ref=e374]: 󰗦 - text: "2026" - generic [ref=e375]: v2026.04.03 - generic: - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip - tooltip ``` # Test source ```ts 1 | import { 2 | IUnit, 3 | IUnitEditGeneralData, 4 | IUnitEditMeasurementsData, 5 | IUnitMoveToStatus, 6 | } from '@/manager/modules/unit/types/unit-types'; 7 | import { 8 | expectUnitDetailsPageAndThenListPageToShowCorrectData, 9 | expectUnitListPageAndThenDetailsPageToShowCorrectData, 10 | } from '@/manager/modules/unit/utils/unit-entity-expect-utils'; 11 | import { UnitListPage } from '@/manager/modules/unit/views/UnitListPage'; 12 | import { expect } from '@/shared/utils/matcher-utils'; 13 | 14 | export async function expectUnitStatusToBeChanged( 15 | startPage: UnitListPage, 16 | oldUnit: IUnit, 17 | newStatus: IUnitMoveToStatus 18 | ): Promise { 19 | const detailsPage = await expectUnitListPageAndThenDetailsPageToShowCorrectData(startPage, oldUnit); 20 | 21 | await detailsPage.moveTo(newStatus); 22 | 23 | const newUnit: IUnit = { 24 | ...oldUnit, 25 | status: newStatus, 26 | }; 27 | 28 | await expectUnitDetailsPageAndThenListPageToShowCorrectData(detailsPage, newUnit); 29 | } 30 | 31 | export async function expectUnitStatusToNotBeChanged( 32 | startPage: UnitListPage, 33 | oldUnit: IUnit, 34 | newStatus: IUnitMoveToStatus 35 | ): Promise { 36 | const detailsPage = await expectUnitListPageAndThenDetailsPageToShowCorrectData(startPage, oldUnit); 37 | > 38 | await detailsPage.actionMenuButton.click(); | ^ Error: locator.click: Test timeout of 60000ms exceeded. 39 | 40 | await expect(detailsPage.getActionMenuMoveToItem(newStatus)).toBeHidden(); 41 | } 42 | 43 | export async function expectUnitMeasurementsToBeEdited( 44 | startPage: UnitListPage, 45 | oldUnit: IUnit, 46 | changes: IUnitEditMeasurementsData 47 | ): Promise { 48 | const oldDetailsPage = await expectUnitListPageAndThenDetailsPageToShowCorrectData(startPage, oldUnit); 49 | 50 | const editDialog = await oldDetailsPage.openUnitEditMeasurementsDialog(); 51 | const newDetailsPage = await editDialog.edit(changes); 52 | 53 | const newUnit: IUnit = { 54 | ...oldUnit, 55 | measurements: { 56 | area: changes.area === null ? undefined : (changes.area ?? oldUnit.measurements.area), 57 | ceilingHeight: 58 | changes.ceilingHeight === null ? undefined : (changes.ceilingHeight ?? oldUnit.measurements.ceilingHeight), 59 | width: changes.width === null ? undefined : (changes.width ?? oldUnit.measurements.width), 60 | depth: changes.depth === null ? undefined : (changes.depth ?? oldUnit.measurements.depth), 61 | volume: changes.volume === null ? undefined : (changes.volume ?? oldUnit.measurements.volume), 62 | doorType: changes.doorType === null ? undefined : (changes.doorType ?? oldUnit.measurements.doorType), 63 | doorWidth: changes.doorWidth === null ? undefined : (changes.doorWidth ?? oldUnit.measurements.doorWidth), 64 | doorHeight: changes.doorHeight === null ? undefined : (changes.doorHeight ?? oldUnit.measurements.doorHeight), 65 | floor: changes.floor === null ? undefined : (changes.floor ?? oldUnit.measurements.floor), 66 | }, 67 | }; 68 | 69 | await expectUnitDetailsPageAndThenListPageToShowCorrectData(newDetailsPage, newUnit); 70 | } 71 | 72 | export async function expectUnitGeneralInfoToBeEdited( 73 | startPage: UnitListPage, 74 | oldUnit: IUnit, 75 | changes: IUnitEditGeneralData 76 | ): Promise { 77 | const oldDetailsPage = await expectUnitListPageAndThenDetailsPageToShowCorrectData(startPage, oldUnit); 78 | 79 | const editDialog = await oldDetailsPage.openUnitEditGeneralDialog(); 80 | const newDetailsPage = await editDialog.edit(changes); 81 | 82 | const newUnit: IUnit = { 83 | ...oldUnit, 84 | name: changes.name ?? oldUnit.name, 85 | location: changes.location ?? oldUnit.location, 86 | unitType: changes.unitType ?? oldUnit.unitType, 87 | note: changes.note === null ? undefined : (changes.note ?? oldUnit.note), 88 | accessPermissions: 89 | changes.accessPermissions === null ? undefined : (changes.accessPermissions ?? oldUnit.accessPermissions), 90 | }; 91 | 92 | await expectUnitDetailsPageAndThenListPageToShowCorrectData(newDetailsPage, newUnit); 93 | } 94 | 95 | export async function expectUnitGeneralInfoToNotBeEdited( 96 | startPage: UnitListPage, 97 | oldUnit: IUnit, 98 | changes: IUnitEditGeneralData 99 | ): Promise { 100 | const detailsPage = await expectUnitListPageAndThenDetailsPageToShowCorrectData(startPage, oldUnit); 101 | 102 | const editDialog = await detailsPage.openUnitEditGeneralDialog(); 103 | await editDialog.fill(changes); 104 | await editDialog.submitButton.click(); 105 | 106 | await expect(editDialog.errors).toHaveCountGreaterThan(0); 107 | } 108 | ```