From 4e770fd326dbd89aa33b3b344e5b290fcafb6bec Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Tue, 28 May 2024 10:29:52 +0200 Subject: [PATCH] :sparkles: Add visual testing to dashboard --- frontend/.gitignore | 2 + .../data/dashboard/create-access-token.json | 8 + .../dashboard/get-access-tokens-empty.json | 1 + .../data/dashboard/get-access-tokens.json | 8 + .../data/dashboard/get-font-variants.json | 15 + .../data/dashboard/get-projects-full.json | 19 + .../dashboard/get-shared-files-empty.json | 1 + .../data/dashboard/get-shared-files.json | 219 +++++++++ .../dashboard/get-team-invitations-empty.json | 1 + .../data/dashboard/get-team-invitations.json | 6 + .../data/dashboard/get-team-members.json | 16 + .../data/dashboard/get-team-recent-files.json | 29 ++ .../data/dashboard/get-team-stats.json | 1 + .../data/dashboard/get-webhooks-empty.json | 1 + .../data/dashboard/get-webhooks.json | 20 + .../data/dashboard/search-files-empty.json | 0 .../data/dashboard/search-files.json | 29 ++ .../logged-in-user/get-teams-complete.json | 48 ++ frontend/playwright/ui/pages/DashboardPage.js | 106 ++++- .../playwright/ui/specs/dashboard.spec.js | 6 +- .../playwright/ui/specs/onboarding.spec.js | 2 +- .../ui/visual-specs/visual-dashboard.spec.js | 423 ++++++++++++++++++ .../src/app/main/ui/dashboard/sidebar.cljs | 20 +- .../src/app/main/ui/dashboard/sidebar.scss | 2 + frontend/src/app/main/ui/dashboard/team.cljs | 3 +- .../src/app/main/ui/settings/sidebar.cljs | 1 + 26 files changed, 976 insertions(+), 11 deletions(-) create mode 100644 frontend/playwright/data/dashboard/create-access-token.json create mode 100644 frontend/playwright/data/dashboard/get-access-tokens-empty.json create mode 100644 frontend/playwright/data/dashboard/get-access-tokens.json create mode 100644 frontend/playwright/data/dashboard/get-font-variants.json create mode 100644 frontend/playwright/data/dashboard/get-projects-full.json create mode 100644 frontend/playwright/data/dashboard/get-shared-files-empty.json create mode 100644 frontend/playwright/data/dashboard/get-shared-files.json create mode 100644 frontend/playwright/data/dashboard/get-team-invitations-empty.json create mode 100644 frontend/playwright/data/dashboard/get-team-invitations.json create mode 100644 frontend/playwright/data/dashboard/get-team-members.json create mode 100644 frontend/playwright/data/dashboard/get-team-recent-files.json create mode 100644 frontend/playwright/data/dashboard/get-team-stats.json create mode 100644 frontend/playwright/data/dashboard/get-webhooks-empty.json create mode 100644 frontend/playwright/data/dashboard/get-webhooks.json create mode 100644 frontend/playwright/data/dashboard/search-files-empty.json create mode 100644 frontend/playwright/data/dashboard/search-files.json create mode 100644 frontend/playwright/data/logged-in-user/get-teams-complete.json create mode 100644 frontend/playwright/ui/visual-specs/visual-dashboard.spec.js diff --git a/frontend/.gitignore b/frontend/.gitignore index d69ed5d6f..8d2f604e1 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -10,3 +10,5 @@ node_modules/ /playwright-report/ /blob-report/ /playwright/.cache/ +visual-dashboard.spec.js-snapshots + diff --git a/frontend/playwright/data/dashboard/create-access-token.json b/frontend/playwright/data/dashboard/create-access-token.json new file mode 100644 index 000000000..395e5a1a9 --- /dev/null +++ b/frontend/playwright/data/dashboard/create-access-token.json @@ -0,0 +1,8 @@ +{ + "~:id": "~u62edaeb8-e212-81ca-8004-80a6f8a42e8e", + "~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", + "~:created-at": "~m1718348381840", + "~:updated-at": "~m1718348381840", + "~:name": "new token", + "~:token": "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.9aFN5YdOI-b-NQPos5uqF8J8b9iMyeri3yYhV5FlHuhNbRwk0YuftA.Dygx9O5-KsAHpuqD.ryTDCqelYOk1XYflTlDGFlzG8VLuElKHSGHdJyJvWqcCUANWzl8cVvezvU2GWg1Piin21KNrcV0TEcHPpDggySRbTn01MOIjw3vTVHdGrlHaVq5VpnWb5hCfs_P9kF7Y2IWOa4da4mM.IulvBQUllnay7clORd-NSg" +} diff --git a/frontend/playwright/data/dashboard/get-access-tokens-empty.json b/frontend/playwright/data/dashboard/get-access-tokens-empty.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-access-tokens-empty.json @@ -0,0 +1 @@ +[] diff --git a/frontend/playwright/data/dashboard/get-access-tokens.json b/frontend/playwright/data/dashboard/get-access-tokens.json new file mode 100644 index 000000000..ec296ea8a --- /dev/null +++ b/frontend/playwright/data/dashboard/get-access-tokens.json @@ -0,0 +1,8 @@ +[ + { + "~:id": "~u62edaeb8-e212-81ca-8004-80a6f8a42e8e", + "~:name": "new token", + "~:created-at": "~m1718348381840", + "~:updated-at": "~m1718348381840" + } +] diff --git a/frontend/playwright/data/dashboard/get-font-variants.json b/frontend/playwright/data/dashboard/get-font-variants.json new file mode 100644 index 000000000..6a16ec574 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-font-variants.json @@ -0,0 +1,15 @@ +[ + { + "~:font-style": "normal", + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:font-id": "~u838cda51-c50f-8032-8004-6ac92ea6eaea", + "~:font-weight": 400, + "~:ttf-file-id": "~ue3710e43-7e40-405d-a4ea-8bb85443d44b", + "~:modified-at": "~m1716880956479", + "~:otf-file-id": "~u72bd3cda-478a-4e0e-a372-4a4f7cdc1371", + "~:id": "~u28f4b65f-3667-8087-8004-6ac93050433a", + "~:woff1-file-id": "~ua4c0a056-2eb6-47cc-bf80-3115d14e048d", + "~:created-at": "~m1716880956479", + "~:font-family": "Milligram Variable Trial" + } +] diff --git a/frontend/playwright/data/dashboard/get-projects-full.json b/frontend/playwright/data/dashboard/get-projects-full.json new file mode 100644 index 000000000..491351e86 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-projects-full.json @@ -0,0 +1,19 @@ +[{ + "~:id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7", + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1715266551088", + "~:modified-at": "~m1715266551088", + "~:is-default": false, + "~:name": "New Project 1", + "~:is-pinned": false, + "~:count": 1 +}, +{ + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1713533116382", + "~:modified-at": "~m1713873823633", + "~:is-default": true, + "~:name": "Drafts", + "~:count": 1 +}] diff --git a/frontend/playwright/data/dashboard/get-shared-files-empty.json b/frontend/playwright/data/dashboard/get-shared-files-empty.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-shared-files-empty.json @@ -0,0 +1 @@ +[] diff --git a/frontend/playwright/data/dashboard/get-shared-files.json b/frontend/playwright/data/dashboard/get-shared-files.json new file mode 100644 index 000000000..3fffa07f4 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-shared-files.json @@ -0,0 +1,219 @@ +{ + "~#set": [ + { + "~:name": "New File 3", + "~:revn": 1, + "~:id": "~u28f4b65f-3667-8087-8004-69eca173cc07", + "~:is-shared": true, + "~:project-id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7", + "~:created-at": "~m1713518796912", + "~:modified-at": "~m1713519762931", + "~:library-summary": { + "~:components": { + "~:count": 1, + "~:sample": [ + { + "~:id": "~ua30724ae-f8d8-8003-8004-69ecacfc8a4c", + "~:name": "Rectangle", + "~:path": "", + "~:modified-at": "~m1716823150739", + "~:main-instance-id": "~ua30724ae-f8d8-8003-8004-69ecacfa2045", + "~:main-instance-page": "~u28f4b65f-3667-8087-8004-69eca173cc08", + "~:objects": { + "~ua30724ae-f8d8-8003-8004-69ecacfa2045": { + "~#shape": { + "~:y": 168, + "~:hide-fill-on-export": false, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:hide-in-viewer": true, + "~:name": "Rectangle", + "~:width": 553, + "~:type": "~:frame", + "~:points": [ + { + "~#point": { + "~:x": 481, + "~:y": 168 + } + }, + { + "~#point": { + "~:x": 1034, + "~:y": 168 + } + }, + { + "~#point": { + "~:x": 1034, + "~:y": 550 + } + }, + { + "~#point": { + "~:x": 481, + "~:y": 550 + } + } + ], + "~:component-root": true, + "~:show-content": true, + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:id": "~ua30724ae-f8d8-8003-8004-69ecacfa2045", + "~:parent-id": "~u00000000-0000-0000-0000-000000000000", + "~:component-id": "~ua30724ae-f8d8-8003-8004-69ecacfc8a4c", + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 481, + "~:main-instance": true, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 481, + "~:y": 168, + "~:width": 553, + "~:height": 382, + "~:x1": 481, + "~:y1": 168, + "~:x2": 1034, + "~:y2": 550 + } + }, + "~:fills": [], + "~:flip-x": null, + "~:height": 382, + "~:component-file": "~u28f4b65f-3667-8087-8004-69eca173cc07", + "~:flip-y": null, + "~:shapes": [ + "~ua30724ae-f8d8-8003-8004-69eca9b27c8c" + ] + } + }, + "~ua30724ae-f8d8-8003-8004-69eca9b27c8c": { + "~#shape": { + "~:y": 168, + "~:rx": 0, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Rectangle", + "~:width": 553, + "~:type": "~:rect", + "~:points": [ + { + "~#point": { + "~:x": 481, + "~:y": 168 + } + }, + { + "~#point": { + "~:x": 1034, + "~:y": 168 + } + }, + { + "~#point": { + "~:x": 1034, + "~:y": 550 + } + }, + { + "~#point": { + "~:x": 481, + "~:y": 550 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:scale", + "~:constraints-h": "~:scale", + "~:id": "~ua30724ae-f8d8-8003-8004-69eca9b27c8c", + "~:parent-id": "~ua30724ae-f8d8-8003-8004-69ecacfa2045", + "~:frame-id": "~ua30724ae-f8d8-8003-8004-69ecacfa2045", + "~:strokes": [], + "~:x": 481, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 481, + "~:y": 168, + "~:width": 553, + "~:height": 382, + "~:x1": 481, + "~:y1": 168, + "~:x2": 1034, + "~:y2": 550 + } + }, + "~:fills": [ + { + "~:fill-color": "#B1B2B5", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:ry": 0, + "~:height": 382, + "~:flip-y": null + } + } + } + } + ] + }, + "~:media": { + "~:count": 0, + "~:sample": [] + }, + "~:colors": { + "~:count": 0, + "~:sample": [] + }, + "~:typographies": { + "~:count": 0, + "~:sample": [] + } + } + } + ] +} diff --git a/frontend/playwright/data/dashboard/get-team-invitations-empty.json b/frontend/playwright/data/dashboard/get-team-invitations-empty.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-team-invitations-empty.json @@ -0,0 +1 @@ +[] diff --git a/frontend/playwright/data/dashboard/get-team-invitations.json b/frontend/playwright/data/dashboard/get-team-invitations.json new file mode 100644 index 000000000..f7ac77543 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-team-invitations.json @@ -0,0 +1,6 @@ +[ + { "~:email": "test1@mail.com", "~:role": "~:editor", "~:expired": true }, + { "~:email": "test2@mail.com", "~:role": "~:editor", "~:expired": false }, + { "~:email": "test3@mail.com", "~:role": "~:admin", "~:expired": true }, + { "~:email": "test4@mail.com", "~:role": "~:admin", "~:expired": false } +] diff --git a/frontend/playwright/data/dashboard/get-team-members.json b/frontend/playwright/data/dashboard/get-team-members.json new file mode 100644 index 000000000..a869d5e34 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-team-members.json @@ -0,0 +1,16 @@ +[ + { + "~:is-admin": true, + "~:email": "foo@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Princesa Leia", + "~:fullname": "Princesa Leia", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", + "~:profile-id": "~uf56647eb-19a7-8115-8003-b6bc939ecd1b", + "~:created-at": "~m1713533116365" + } +] diff --git a/frontend/playwright/data/dashboard/get-team-recent-files.json b/frontend/playwright/data/dashboard/get-team-recent-files.json new file mode 100644 index 000000000..920bb2df0 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-team-recent-files.json @@ -0,0 +1,29 @@ +[ + { + "~:id": "~u8b479b80-e02d-8074-8004-4088dc6bfd11", + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:created-at": "~m1714045521389", + "~:modified-at": "~m1714045654874", + "~:name": "New File 2", + "~:revn": 1, + "~:is-shared": false + }, + { + "~:id": "~u95d6fdd8-48d8-8148-8004-38af910d2dbe", + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:created-at": "~m1713518796912", + "~:modified-at": "~m1713519762931", + "~:name": "New File 1", + "~:revn": 1, + "~:is-shared": false + }, + { + "~:id": "~u28f4b65f-3667-8087-8004-69eca173cc07", + "~:project-id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7", + "~:created-at": "~m1713518796912", + "~:modified-at": "~m1713519762931", + "~:name": "New File 3", + "~:revn": 1, + "~:is-shared": true + } +] diff --git a/frontend/playwright/data/dashboard/get-team-stats.json b/frontend/playwright/data/dashboard/get-team-stats.json new file mode 100644 index 000000000..c984f1021 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-team-stats.json @@ -0,0 +1 @@ +{"~:projects":1,"~:files":3} diff --git a/frontend/playwright/data/dashboard/get-webhooks-empty.json b/frontend/playwright/data/dashboard/get-webhooks-empty.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-webhooks-empty.json @@ -0,0 +1 @@ +[] diff --git a/frontend/playwright/data/dashboard/get-webhooks.json b/frontend/playwright/data/dashboard/get-webhooks.json new file mode 100644 index 000000000..3849e3608 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-webhooks.json @@ -0,0 +1,20 @@ +[ + { + "~:id": "~u29ce7ec9-e75d-81b4-8004-08100373558a", + "~:uri": { + "~#uri": "https://www.abc.es" + }, + "~:mtype": "application/json", + "~:is-active": false, + "~:error-count": 0 + }, + { + "~:id": "~u43d6b3b1-40f7-807b-8003-f9846292b4c7", + "~:uri": { + "~#uri": "https://www.google.com" + }, + "~:mtype": "application/json", + "~:is-active": true, + "~:error-count": 0 + } +] diff --git a/frontend/playwright/data/dashboard/search-files-empty.json b/frontend/playwright/data/dashboard/search-files-empty.json new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/playwright/data/dashboard/search-files.json b/frontend/playwright/data/dashboard/search-files.json new file mode 100644 index 000000000..920bb2df0 --- /dev/null +++ b/frontend/playwright/data/dashboard/search-files.json @@ -0,0 +1,29 @@ +[ + { + "~:id": "~u8b479b80-e02d-8074-8004-4088dc6bfd11", + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:created-at": "~m1714045521389", + "~:modified-at": "~m1714045654874", + "~:name": "New File 2", + "~:revn": 1, + "~:is-shared": false + }, + { + "~:id": "~u95d6fdd8-48d8-8148-8004-38af910d2dbe", + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:created-at": "~m1713518796912", + "~:modified-at": "~m1713519762931", + "~:name": "New File 1", + "~:revn": 1, + "~:is-shared": false + }, + { + "~:id": "~u28f4b65f-3667-8087-8004-69eca173cc07", + "~:project-id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7", + "~:created-at": "~m1713518796912", + "~:modified-at": "~m1713519762931", + "~:name": "New File 3", + "~:revn": 1, + "~:is-shared": true + } +] diff --git a/frontend/playwright/data/logged-in-user/get-teams-complete.json b/frontend/playwright/data/logged-in-user/get-teams-complete.json new file mode 100644 index 000000000..910e1543f --- /dev/null +++ b/frontend/playwright/data/logged-in-user/get-teams-complete.json @@ -0,0 +1,48 @@ +[ + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:name": "Default", + "~:modified-at": "~m1713533116375", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1713533116375", + "~:is-default": true +}, + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:name": "Second team", + "~:modified-at": "~m1701164272671", + "~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:created-at": "~m1701164272671", + "~:is-default": false + } +] diff --git a/frontend/playwright/ui/pages/DashboardPage.js b/frontend/playwright/ui/pages/DashboardPage.js index e83c62dd9..fbc373d3f 100644 --- a/frontend/playwright/ui/pages/DashboardPage.js +++ b/frontend/playwright/ui/pages/DashboardPage.js @@ -50,6 +50,8 @@ export class DashboardPage extends BaseWebSocketPage { static anyTeamId = "c7ce0794-0992-8105-8004-38e630f40f6d"; + static secondTeamId = "dd33ff88-f4e5-8033-8003-8096cc07bdf3"; + static draftProjectId = "c7ce0794-0992-8105-8004-38e630f7920b"; constructor(page) { @@ -60,12 +62,34 @@ export class DashboardPage extends BaseWebSocketPage { this.draftTitle = page.getByRole("heading", { name: "Drafts" }); this.draftLink = page.getByTestId("drafts-link-sidebar"); this.draftsFile = page.getByText(/New File 1/); + this.fontsLink = page.getByTestId("fonts-link-sidebar"); + this.fontsTitle = page.getByRole("heading", { name: "Fonts", level: 1 }); + this.libsLink = page.getByTestId("libs-link-sidebar"); + this.libsTitle = page.getByRole("heading", { name: "Libraries", level: 1 }); + this.searchButton = page.getByRole("button", { name: "dashboard-search" }); + this.searchTitle = page.getByRole("heading", { name: "Search results" }); + this.searchInput = page.getByPlaceholder('Search…'); + this.newFileName = page.getByText("New File 3"); + this.teamDropdown = page.getByRole('button', { name: 'Your Penpot' }); + this.userAccount = page.getByRole('button', { name: "Princesa Leia Princesa Leia" }); + this.userProfileOption = page.getByText("Your account"); + this.userAccountTitle = page.getByRole("heading", {name: "Your account"}); } async setupDraftsEmpty() { await this.mockRPC("get-project-files?project-id=*", "dashboard/get-project-files-empty.json"); } + async setupSearchEmpty() { + await this.mockRPC("search-files", "dashboard/search-files-empty.json", { + method: "POST", + }); + } + + async setupLibrariesEmpty() { + await this.mockRPC("get-team-shared-files?team-id=*", "dashboard/get-shared-files-empty.json"); + } + async setupDrafts() { await this.mockRPC("get-project-files?project-id=*", "dashboard/get-project-files.json"); } @@ -74,15 +98,95 @@ export class DashboardPage extends BaseWebSocketPage { await this.mockRPC("create-project", "dashboard/create-project.json", { method: "POST" }); await this.mockRPC("get-projects?team-id=*", "dashboard/get-projects-new.json"); } - async goToWorkspace() { + + async setupDashboardFull() { + await this.mockRPC("get-projects?team-id=*", "dashboard/get-projects-full.json"); + await this.mockRPC("get-project-files?project-id=*", "dashboard/get-project-files.json"); + await this.mockRPC("get-team-shared-files?team-id=*", "dashboard/get-shared-files.json"); + await this.mockRPC("get-team-shared-files?project-id=*", "dashboard/get-shared-files.json"); + await this.mockRPC("get-team-recent-files?team-id=*", "dashboard/get-team-recent-files.json"); + await this.mockRPC("get-font-variants?team-id=*", "dashboard/get-font-variants.json"); + await this.mockRPC("search-files", "dashboard/search-files.json", { method: "POST" }); + await this.mockRPC("search-files", "dashboard/search-files.json" ); + await this.mockRPC("get-teams", "logged-in-user/get-teams-complete.json"); + } + + async setupAccessTokensEmpty() { + await this.mockRPC("get-access-tokens", "dashboard/get-access-tokens-empty.json"); + } + + async createAccessToken() { + await this.mockRPC("create-access-token", "dashboard/create-access-token.json", { method: "POST" }); + } + + async setupAccessTokens() { + await this.mockRPC("get-access-tokens", "dashboard/get-access-tokens.json"); + } + + async setupTeamInvitationsEmpty() { + await this.mockRPC("get-team-invitations?team-id=*", "dashboard/get-team-invitations-empty.json"); + } + + async setupTeamInvitations() { + await this.mockRPC("get-team-invitations?team-id=*", "dashboard/get-team-invitations.json"); + } + + async setupTeamWebhooksEmpty() { + await this.mockRPC("get-webhooks?team-id=*", "dashboard/get-webhooks-empty.json"); + } + + async setupTeamWebhooks() { + await this.mockRPC("get-webhooks?team-id=*", "dashboard/get-webhooks.json"); + } + + async setupTeamSettings() { + await this.mockRPC("get-team-stats?team-id=*", "dashboard/get-team-stats.json"); + } + + async goToDashboard() { await this.page.goto(`#/dashboard/team/${DashboardPage.anyTeamId}/projects`); } + async goToSecondTeamDashboard() { + await this.page.goto(`#/dashboard/team/${DashboardPage.secondTeamId}/projects`); + } + + async goToSecondTeamMembersSection() { + await this.page.goto(`#/dashboard/team/${DashboardPage.secondTeamId}/members`); + } + + async goToSecondTeamInvitationsSection() { + await this.page.goto(`#/dashboard/team/${DashboardPage.secondTeamId}/invitations`); + } + + async goToSecondTeamWebhooksSection() { + await this.page.goto(`#/dashboard/team/${DashboardPage.secondTeamId}/webhooks`); + } + + async goToSecondTeamWebhooksSection() { + await this.page.goto(`#/dashboard/team/${DashboardPage.secondTeamId}/webhooks`); + } + + async goToSecondTeamSettingsSection() { + await this.page.goto(`#/dashboard/team/${DashboardPage.secondTeamId}/settings`); + } + + async goToSearch() { + await this.page.goto(`#/dashboard/team/${DashboardPage.anyTeamId}/search`); + } + async goToDrafts() { await this.page.goto( `#/dashboard/team/${DashboardPage.anyTeamId}/projects/${DashboardPage.draftProjectId}`, ); } + + async goToAccount() { + + await this.userAccount.click(); + + await this.userProfileOption.click(); + } } export default DashboardPage; diff --git a/frontend/playwright/ui/specs/dashboard.spec.js b/frontend/playwright/ui/specs/dashboard.spec.js index 23e71efad..4cb381b4a 100644 --- a/frontend/playwright/ui/specs/dashboard.spec.js +++ b/frontend/playwright/ui/specs/dashboard.spec.js @@ -13,7 +13,7 @@ test.beforeEach(async ({ page }) => { test("Dashboad page has title ", async ({ page }) => { const dashboardPage = new DashboardPage(page); - await dashboardPage.goToWorkspace(); + await dashboardPage.goToDashboard(); await expect(dashboardPage.page).toHaveURL(/dashboard/); await expect(dashboardPage.titleLabel).toBeVisible(); @@ -23,7 +23,7 @@ test("User can create a new project", async ({ page }) => { const dashboardPage = new DashboardPage(page); await dashboardPage.setupNewProject(); - await dashboardPage.goToWorkspace(); + await dashboardPage.goToDashboard(); await dashboardPage.addProjectBtn.click(); await expect(dashboardPage.projectName).toBeVisible(); @@ -33,7 +33,7 @@ test("User goes to draft page", async ({ page }) => { const dashboardPage = new DashboardPage(page); await dashboardPage.setupDraftsEmpty(); - await dashboardPage.goToWorkspace(); + await dashboardPage.goToDashboard(); await dashboardPage.draftLink.click(); await expect(dashboardPage.draftTitle).toBeVisible(); diff --git a/frontend/playwright/ui/specs/onboarding.spec.js b/frontend/playwright/ui/specs/onboarding.spec.js index 39efa967c..68e00fc50 100644 --- a/frontend/playwright/ui/specs/onboarding.spec.js +++ b/frontend/playwright/ui/specs/onboarding.spec.js @@ -12,7 +12,7 @@ test("User can complete the onboarding", async ({ page }) => { const dashboardPage = new DashboardPage(page); const onboardingPage = new OnboardingPage(page); - await dashboardPage.goToWorkspace(); + await dashboardPage.goToDashboard(); await expect(page.getByRole("heading", { name: "Help us get to know you" })).toBeVisible(); await onboardingPage.fillOnboardingInputsStep1(); diff --git a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js new file mode 100644 index 000000000..faefbeb1b --- /dev/null +++ b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js @@ -0,0 +1,423 @@ +import { test, expect } from "@playwright/test"; +import DashboardPage from "../pages/DashboardPage"; + +test.beforeEach(async ({ page }) => { + await DashboardPage.init(page); + await DashboardPage.mockRPC( + page, + "get-profile", + "logged-in-user/get-profile-logged-in-no-onboarding.json", + ); +}); + +test("User goes to an empty dashboard", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await expect(dashboardPage.titleLabel).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +// Empty dashboard pages + +test("User goes to an empty draft page", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDraftsEmpty(); + + await dashboardPage.goToDashboard(); + await dashboardPage.draftLink.click(); + + await expect(dashboardPage.draftTitle).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an empty fonts page", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + await dashboardPage.fontsLink.click(); + + await expect(dashboardPage.fontsTitle).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an empty libraries page", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupLibrariesEmpty(); + + await dashboardPage.goToDashboard(); + await dashboardPage.libsLink.click(); + + await expect(dashboardPage.libsTitle).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an empty search page", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupSearchEmpty(); + + await dashboardPage.goToSearch(); + + await expect(dashboardPage.searchTitle).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to the dashboard with a new project", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupNewProject(); + + await dashboardPage.goToDashboard(); + + await expect(dashboardPage.projectName).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +// Dashboard pages with content + +test("User goes to a full dashboard", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToDashboard(); + + await expect(dashboardPage.draftsFile).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an full draft page", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToDashboard(); + await dashboardPage.draftLink.click(); + + await expect(dashboardPage.draftTitle).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an full library page", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToDashboard(); + await dashboardPage.libsLink.click(); + + await expect(dashboardPage.libsTitle).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an full fonts page", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToDashboard(); + await dashboardPage.fontsLink.click(); + + await expect(dashboardPage.fontsTitle).toBeVisible(); + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an full search page", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToSearch(); + + await expect(dashboardPage.searchInput).toBeVisible(); + + await dashboardPage.searchInput.fill("New"); + + await expect(dashboardPage.searchTitle).toBeVisible(); + + await expect(dashboardPage.newFileName).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +// Account management + +test("User opens user account", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await expect(dashboardPage.userAccount).toBeVisible(); + + await dashboardPage.goToAccount(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to user profile", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await dashboardPage.goToAccount(); + + await expect(dashboardPage.userAccountTitle).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to password management section", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await dashboardPage.goToAccount(); + + await page.getByText("Password").click(); + + await expect(page.getByRole("heading", { name: "Change Password" })).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to settings section", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await dashboardPage.goToAccount(); + + await page.getByTestId("settings-profile").click(); + + await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an empty access tokens secction", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await dashboardPage.setupAccessTokensEmpty(); + + await dashboardPage.goToAccount(); + + await page.getByText("Access tokens").click(); + + await expect(page.getByRole("heading", { name: "Personal access tokens" })).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User can create an access token", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await dashboardPage.setupAccessTokensEmpty(); + + await dashboardPage.goToAccount(); + + await page.getByText("Access tokens").click(); + + await expect(page.getByRole("heading", { name: "Personal access tokens" })).toBeVisible(); + + await page.getByRole("button", { name: "Generate New Token" }).click(); + + await dashboardPage.createAccessToken(); + + await expect(page.getByPlaceholder("The name can help to know")).toBeVisible(); + + await page.getByPlaceholder("The name can help to know").fill("New token"); + + await expect(page.getByRole("button", { name: "Create token" })).not.toBeDisabled(); + + await page.getByRole("button", { name: "Create token" }).click(); + + await expect(page.getByRole("button", { name: "Create token" })).not.toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to a full access tokens secction", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await dashboardPage.setupAccessTokens(); + + await dashboardPage.goToAccount(); + + await page.getByText("Access tokens").click(); + + await expect(page.getByRole("heading", { name: "Personal access tokens" })).toBeVisible(); + + await expect(page.getByText("new token", { exact: true })).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to the feedback secction", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await dashboardPage.goToAccount(); + + await page.getByText("Give feedback").click(); + + await expect(page.getByRole("heading", { name: "Email" })).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +// Teams management + +test("User opens teams selector with only one team", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + + await dashboardPage.goToDashboard(); + + await expect(dashboardPage.titleLabel).toBeVisible(); + + await dashboardPage.teamDropdown.click(); + + await expect(page.getByText("Create new team")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User opens teams selector with more than one team", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToDashboard(); + + await expect(dashboardPage.titleLabel).toBeVisible(); + + await dashboardPage.teamDropdown.click(); + + await expect(page.getByText("Second Team")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to second team", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToDashboard(); + + await dashboardPage.teamDropdown.click(); + + await expect(page.getByText("Second Team")).toBeVisible(); + + await page.getByText("Second Team").click(); + + await expect(page.getByText("Team Up")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User opens team management dropdown", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToSecondTeamDashboard(); + + await expect(page.getByText("Team Up")).toBeVisible(); + + await page.getByRole("button", { name: "team-management" }).click(); + + await expect(page.getByTestId("team-members")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to team management section", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToSecondTeamMembersSection(); + + await expect(page.getByText("role")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an empty invitations section", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamInvitationsEmpty(); + + await dashboardPage.goToSecondTeamInvitationsSection(); + + await expect(page.getByText("No pending invitations")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to a complete invitations section", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamInvitations(); + + await dashboardPage.goToSecondTeamInvitationsSection(); + + await expect(page.getByText("test1@mail.com")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + + +test("User invite people to the team", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamInvitationsEmpty(); + + await dashboardPage.goToSecondTeamInvitationsSection(); + + await expect(page.getByTestId("invite-member")).toBeVisible(); + + await page.getByTestId("invite-member").click(); + + await expect(page.getByText("Invite with the role")).toBeVisible(); + + await page.getByPlaceholder('Emails, comma separated').fill("test5@mail.com"); + + await expect(page.getByText("Send invitation")).not.toBeDisabled(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to an empty webhook section", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamWebhooksEmpty(); + + await dashboardPage.goToSecondTeamWebhooksSection(); + + await expect(page.getByText("No webhooks created so far.")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to a complete webhook section", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamWebhooks(); + + await dashboardPage.goToSecondTeamWebhooksSection(); + + await expect(page.getByText("https://www.google.com")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); + +test("User goes to the team settings section", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamSettings(); + + await dashboardPage.goToSecondTeamSettingsSection(); + + await expect(page.getByText("TEAM INFO")).toBeVisible(); + + await expect(dashboardPage.page).toHaveScreenshot(); +}); diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 10cfd2f5c..0862eb17e 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -256,11 +256,13 @@ (if (or @focused? (seq search-term)) [:button {:class (stl/css :search-btn :clear-search-btn) :tab-index "0" + :aria-label "dashboard-clear-search" :on-click on-clear-click :on-key-down handle-clear-search} clear-search-icon] [:button {:class (stl/css :search-btn) + :aria-label "dashboard-search" :on-click on-clear-click} search-icon])])) @@ -504,11 +506,13 @@ :on-key-down handle-members :className (stl/css :team-options-item) :id "teams-options-members" + :data-testid "team-members" :data-test "team-members"} (tr "labels.members")] [:> dropdown-menu-item* {:on-click go-invitations :on-key-down handle-invitations :className (stl/css :team-options-item) + :data-testid "team-invitations" :id "teams-options-invitations" :data-test "team-invitations"} (tr "labels.invitations")] @@ -524,6 +528,7 @@ :on-key-down handle-settings :className (stl/css :team-options-item) :id "teams-options-settings" + :data-testid "team-settings" :data-test "team-settings"} (tr "labels.settings")] @@ -533,6 +538,7 @@ :on-key-down handle-rename :id "teams-options-rename" :className (stl/css :team-options-item) + :data-testid "rename-team" :data-test "rename-team"} (tr "labels.rename")]) @@ -550,6 +556,7 @@ :on-key-down handle-leave-as-owner-clicked :id "teams-options-leave-team" :className (stl/css :team-options-item) + :data-testid "leave-team" :data-test "leave-team"} (tr "dashboard.leave-team")] @@ -654,6 +661,7 @@ (when-not (:is-default team) [:button {:class (stl/css :switch-options) :on-click handle-show-opts-click + :aria-label "team-management" :tab-index "0" :on-key-down handle-show-opts-keydown} menu-icon])] @@ -792,6 +800,7 @@ [:li {:class (stl/css-case :current libs? :sidebar-nav-item true)} [:& link {:action go-libs + :data-testid "libs-link-sidebar" :class (stl/css :sidebar-link) :keyboard-action go-libs-with-key} [:span {:class (stl/css :element-title)} (tr "labels.shared-libraries")]]]]] @@ -803,6 +812,7 @@ :current fonts?)} [:& link {:action go-fonts :class (stl/css :sidebar-link) + :data-testid "fonts-link-sidebar" :keyboard-action go-fonts-with-key :data-test "fonts"} [:span {:class (stl/css :element-title)} (tr "labels.fonts")]]]]] @@ -946,11 +956,11 @@ :on-hide-comments handle-hide-comments}]) [:div {:class (stl/css :profile-section)} - [:div {:class (stl/css :profile) - :tab-index "0" - :on-click handle-click - :on-key-down handle-key-down - :data-test "profile-btn"} + [:button {:class (stl/css :profile) + :tab-index "0" + :on-click handle-click + :on-key-down handle-key-down + :data-test "profile-btn"} [:img {:src photo :class (stl/css :profile-img) :alt (:fullname profile)}] diff --git a/frontend/src/app/main/ui/dashboard/sidebar.scss b/frontend/src/app/main/ui/dashboard/sidebar.scss index f939c8d44..22a0a3c9c 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.scss +++ b/frontend/src/app/main/ui/dashboard/sidebar.scss @@ -331,10 +331,12 @@ } .profile { + @include buttonStyle; display: grid; grid-template-columns: auto 1fr; gap: $s-8; cursor: pointer; + text-align: left; } .profile-fullname { diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 76652e98a..380a07da2 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -105,7 +105,8 @@ [:a {:class (stl/css :btn-secondary :btn-small) :on-click on-invite-member - :data-test "invite-member"} + :data-test "invite-member" + :data-testid "invite-member"} (tr "dashboard.invite-profile")] [:div {:class (stl/css :blank-space)}])]])) diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index c878c7b4b..6b6bed60d 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -89,6 +89,7 @@ [:li {:class (stl/css-case :current options? :settings-item true) :on-click go-settings-options + :data-testid "settings-profile" :data-test "settings-profile"} [:span {:class (stl/css :element-title)} (tr "labels.settings")]]