diff --git a/frontend/playwright/data/subscription/get-owned-teams.json b/frontend/playwright/data/subscription/get-owned-teams.json new file mode 100644 index 0000000000..e714a37a7c --- /dev/null +++ b/frontend/playwright/data/subscription/get-owned-teams.json @@ -0,0 +1,12 @@ +[ + { + "~:id": "~uf88e52d7-2b77-81fd-8006-23413fafe56c", + "~:name": "The Alpaca team", + "~:total-members": 3 + }, + { + "~:id": "~u81be1d05-a07b-81d5-8006-3e728bea76fb", + "~:name": "The Quokka team", + "~:total-members": 1 + } +] \ No newline at end of file diff --git a/frontend/playwright/data/subscription/get-profile-enterprise-subscription.json b/frontend/playwright/data/subscription/get-profile-enterprise-subscription.json new file mode 100644 index 0000000000..75fc221631 --- /dev/null +++ b/frontend/playwright/data/subscription/get-profile-enterprise-subscription.json @@ -0,0 +1,25 @@ +{ + "~:email": "foo@example.com", + "~:is-demo": false, + "~:auth-backend": "penpot", + "~:fullname": "Princesa Leia", + "~:modified-at": "~m1713533116365", + "~:is-active": true, + "~:default-project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", + "~:is-muted": false, + "~:default-team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1713533116365", + "~:is-blocked": false, + "~:props": { + "~:subscription": { + "~:quantity": 2, + "~:status": "trialing", + "~:type": "enterprise", + "~start-date": "~m1746444667" + }, + "~:v2-info-shown": true, + "~:viewed-tutorial?": false, + "~:viewed-walkthrough?": false + } +} diff --git a/frontend/playwright/data/subscription/get-profile-unlimited-subscription.json b/frontend/playwright/data/subscription/get-profile-unlimited-subscription.json new file mode 100644 index 0000000000..5d56af4ac9 --- /dev/null +++ b/frontend/playwright/data/subscription/get-profile-unlimited-subscription.json @@ -0,0 +1,25 @@ +{ + "~:email": "foo@example.com", + "~:is-demo": false, + "~:auth-backend": "penpot", + "~:fullname": "Princesa Leia", + "~:modified-at": "~m1713533116365", + "~:is-active": true, + "~:default-project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", + "~:is-muted": false, + "~:default-team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1713533116365", + "~:is-blocked": false, + "~:props": { + "~:subscription": { + "~:quantity": 2, + "~:status": "trialing", + "~:type": "unlimited", + "~start-date": "~m1746444667" + }, + "~:v2-info-shown": true, + "~:viewed-tutorial?": false, + "~:viewed-walkthrough?": false + } +} diff --git a/frontend/playwright/data/subscription/get-team-info-subscription.json b/frontend/playwright/data/subscription/get-team-info-subscription.json new file mode 100644 index 0000000000..c3ea011ec0 --- /dev/null +++ b/frontend/playwright/data/subscription/get-team-info-subscription.json @@ -0,0 +1,4 @@ +{ + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920a", + "~:is-default": false +} diff --git a/frontend/playwright/data/subscription/get-team-members-more-than-8.json b/frontend/playwright/data/subscription/get-team-members-more-than-8.json new file mode 100644 index 0000000000..55210eeff2 --- /dev/null +++ b/frontend/playwright/data/subscription/get-team-members-more-than-8.json @@ -0,0 +1,128 @@ +[ + { + "~:is-admin": true, + "~:email": "bar@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Han Solo", + "~:fullname": "Han Solo", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~u1e162163-87b7-805b-8005-5fd05514b6d3", + "~:profile-id": "~u1e162163-87b7-805b-8005-5fd05514b6d3", + "~:created-at": "~m1733324626956" + }, + { + "~:is-admin": true, + "~:email": "foo@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Princesa Leia", + "~:fullname": "Princesa Leia", + "~:is-owner": true, + "~: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" + }, + { + "~:is-admin": false, + "~:email": "luke@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Luke Skywalker", + "~:fullname": "Luke Skywalker", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~u3a1b2c3d-1234-5678-8001-abcdefabcdef", + "~:profile-id": "~u3a1b2c3d-1234-5678-8001-abcdefabcdef", + "~:created-at": "~m1713533116365" + }, + { + "~:is-admin": false, + "~:email": "chewie@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Chewbacca", + "~:fullname": "Chewbacca", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": false, + "~:is-active": true, + "~:id": "~u4b2c3d4e-2345-6789-8002-bcdefabcdefa", + "~:profile-id": "~u4b2c3d4e-2345-6789-8002-bcdefabcdefa", + "~:created-at": "~m1713533116365" + }, + { + "~:is-admin": false, + "~:email": "lando@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Lando Calrissian", + "~:fullname": "Lando Calrissian", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~u5c3d4e5f-3456-7890-8003-cdefabcdefab", + "~:profile-id": "~u5c3d4e5f-3456-7890-8003-cdefabcdefab", + "~:created-at": "~m1713533116365" + }, + { + "~:is-admin": false, + "~:email": "r2d2@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "R2-D2", + "~:fullname": "R2-D2", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": false, + "~:is-active": true, + "~:id": "~u6d4e5f6a-4567-8901-8004-defabcdefabc", + "~:profile-id": "~u6d4e5f6a-4567-8901-8004-defabcdefabc", + "~:created-at": "~m1713533116365" + }, + { + "~:is-admin": false, + "~:email": "c3po@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "C-3PO", + "~:fullname": "C-3PO", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": false, + "~:is-active": true, + "~:id": "~u7e5f6a7b-5678-9012-8005-efabcdefabcd", + "~:profile-id": "~u7e5f6a7b-5678-9012-8005-efabcdefabcd", + "~:created-at": "~m1713533116365" + }, + { + "~:is-admin": false, + "~:email": "ben@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Ben Kenobi", + "~:fullname": "Ben Kenobi", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~u8f6a7b8c-6789-0123-8006-fabcdefabcde", + "~:profile-id": "~u8f6a7b8c-6789-0123-8006-fabcdefabcde", + "~:created-at": "~m1713533116365" + }, + { + "~:is-admin": false, + "~:email": "yoda@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Yoda", + "~:fullname": "Yoda", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": false, + "~:is-active": true, + "~:id": "~u9a7b8c9d-7890-1234-8007-abcdefabcdef", + "~:profile-id": "~u9a7b8c9d-7890-1234-8007-abcdefabcdef", + "~:created-at": "~m1713533116365" + } +] diff --git a/frontend/playwright/data/subscription/get-team-members-subscription-member.json b/frontend/playwright/data/subscription/get-team-members-subscription-member.json new file mode 100644 index 0000000000..04237e11f8 --- /dev/null +++ b/frontend/playwright/data/subscription/get-team-members-subscription-member.json @@ -0,0 +1,30 @@ +[ + { + "~:is-admin": true, + "~:email": "bar@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Han Solo", + "~:fullname": "Han Solo", + "~:is-owner": true, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~u1e162163-87b7-805b-8005-5fd05514b6d3", + "~:profile-id": "~u1e162163-87b7-805b-8005-5fd05514b6d3", + "~:created-at": "~m1733324626956" + }, + { + "~: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/subscription/get-team-members-subscription-owner.json b/frontend/playwright/data/subscription/get-team-members-subscription-owner.json new file mode 100644 index 0000000000..16bbf41d8d --- /dev/null +++ b/frontend/playwright/data/subscription/get-team-members-subscription-owner.json @@ -0,0 +1,30 @@ +[ + { + "~:is-admin": true, + "~:email": "bar@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Han Solo", + "~:fullname": "Han Solo", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~u1e162163-87b7-805b-8005-5fd05514b6d3", + "~:profile-id": "~u1e162163-87b7-805b-8005-5fd05514b6d3", + "~:created-at": "~m1733324626956" + }, + { + "~:is-admin": true, + "~:email": "foo@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Princesa Leia", + "~:fullname": "Princesa Leia", + "~:is-owner": true, + "~: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/subscription/get-teams-enterprise-one-team.json b/frontend/playwright/data/subscription/get-teams-enterprise-one-team.json new file mode 100644 index 0000000000..84e860bf51 --- /dev/null +++ b/frontend/playwright/data/subscription/get-teams-enterprise-one-team.json @@ -0,0 +1,27 @@ +[{ + "~: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 + }, + "~:subscription": { + "~:type": "enterprise", + "~:status": "trialing" + }, + "~:name": "Default", + "~:modified-at": "~m1713533116375", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920a", + "~:created-at": "~m1713533116375", + "~:is-default": true +}] diff --git a/frontend/playwright/data/subscription/get-teams-professional-subscription-owner.json b/frontend/playwright/data/subscription/get-teams-professional-subscription-owner.json new file mode 100644 index 0000000000..450a06b2f6 --- /dev/null +++ b/frontend/playwright/data/subscription/get-teams-professional-subscription-owner.json @@ -0,0 +1,52 @@ +[ + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:owner", + "~: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 + }, + "~:subscription": { + "~:type": "professional", + "~:status": "active" + }, + "~:name": "Second team", + "~:modified-at": "~m1701164272671", + "~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:created-at": "~m1701164272671", + "~:is-default": false + } +] diff --git a/frontend/playwright/data/subscription/get-teams-unlimited-one-team.json b/frontend/playwright/data/subscription/get-teams-unlimited-one-team.json new file mode 100644 index 0000000000..3f5d3ca43a --- /dev/null +++ b/frontend/playwright/data/subscription/get-teams-unlimited-one-team.json @@ -0,0 +1,27 @@ +[{ + "~: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 + }, + "~:subscription": { + "~:type": "unlimited", + "~:status": "trialing" + }, + "~:name": "Default", + "~:modified-at": "~m1713533116375", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920a", + "~:created-at": "~m1713533116375", + "~:is-default": true +}] diff --git a/frontend/playwright/data/subscription/get-teams-unlimited-subscription-expired-owner.json b/frontend/playwright/data/subscription/get-teams-unlimited-subscription-expired-owner.json new file mode 100644 index 0000000000..a74b844fb1 --- /dev/null +++ b/frontend/playwright/data/subscription/get-teams-unlimited-subscription-expired-owner.json @@ -0,0 +1,52 @@ +[ + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:owner", + "~: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": "~:owner", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:subscription": { + "~:type": "unlimited", + "~:status": "paused" + }, + "~:name": "Second team", + "~:modified-at": "~m1701164272671", + "~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:created-at": "~m1701164272671", + "~:is-default": false + } +] diff --git a/frontend/playwright/data/subscription/get-teams-unlimited-subscription-member.json b/frontend/playwright/data/subscription/get-teams-unlimited-subscription-member.json new file mode 100644 index 0000000000..3f05a8bf01 --- /dev/null +++ b/frontend/playwright/data/subscription/get-teams-unlimited-subscription-member.json @@ -0,0 +1,52 @@ +[ + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:owner", + "~: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": false, + "~:is-admin": true, + "~:can-edit": true + }, + "~:subscription": { + "~:type": "unlimited", + "~:status": "trialing" + }, + "~:name": "Second team", + "~:modified-at": "~m1701164272671", + "~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:created-at": "~m1701164272671", + "~:is-default": false + } +] diff --git a/frontend/playwright/data/subscription/get-teams-unlimited-subscription-owner.json b/frontend/playwright/data/subscription/get-teams-unlimited-subscription-owner.json new file mode 100644 index 0000000000..aab433c348 --- /dev/null +++ b/frontend/playwright/data/subscription/get-teams-unlimited-subscription-owner.json @@ -0,0 +1,52 @@ +[ + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:owner", + "~: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": "~:owner", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:subscription": { + "~:type": "unlimited", + "~:status": "trialing" + }, + "~: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/SubscriptionProfilePage.js b/frontend/playwright/ui/pages/SubscriptionProfilePage.js new file mode 100644 index 0000000000..d37e625045 --- /dev/null +++ b/frontend/playwright/ui/pages/SubscriptionProfilePage.js @@ -0,0 +1,31 @@ +import { expect } from "@playwright/test"; +import { BaseWebSocketPage } from "./BaseWebSocketPage"; + +export class SubscriptionProfilePage extends BaseWebSocketPage { + static async init(page) { + await BaseWebSocketPage.initWebSockets(page); + + await BaseWebSocketPage.mockRPC( + page, + "get-owned-teams", + "subscription/get-owned-teams.json", + ); + + } + + constructor(page) { + super(page); + + this.mainHeading = page.getByRole('heading', { name: 'Subscription', level: 2 }); + } + + async goToSubscriptions() { + await this.page.goto( + `#/settings/subscriptions`, + ); + await expect(this.mainHeading).toBeVisible(); + } + +} + +export default SubscriptionProfilePage; diff --git a/frontend/playwright/ui/specs/subscriptions.spec.js b/frontend/playwright/ui/specs/subscriptions.spec.js new file mode 100644 index 0000000000..58742bb1d5 --- /dev/null +++ b/frontend/playwright/ui/specs/subscriptions.spec.js @@ -0,0 +1,712 @@ +import { test, expect } from "@playwright/test"; +import DashboardPage from "../pages/DashboardPage"; +import { WorkspacePage } from "../pages/WorkspacePage"; +import { SubscriptionProfilePage } from "../pages/SubscriptionProfilePage"; + +test.describe("Subscriptions: dashboard", () => { + test("Team with unlimited subscription has specific icon in menu", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-owner.json", + ); + + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + await dashboardPage.goToSecondTeamDashboard(); + await expect(page.getByTestId("subscription-icon")).toBeVisible(); + }); + + test("The Unlimited subscription has its name in the sidebar dropdown", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-owner.json", + ); + + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + await dashboardPage.goToDashboard(); + + await expect(page.getByTestId("subscription-name")).toHaveText( + "Unlimited plan (trial)", + ); + }); +}); + +test.describe("Subscriptions: Team members and invitations", () => { + test("Team settings has susbscription name and no manage subscription link when is member", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "logged-in-user/get-profile-logged-in.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-member.json", + ); + + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-members?team-id=*", + "subscription/get-team-members-subscription-member.json", + ); + + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + + await dashboardPage.goToSecondTeamSettingsSection(); + await expect(page.getByText("Unlimited (trial)")).toBeVisible(); + await expect( + page.getByRole("button", { name: "Manage your subscription" }), + ).not.toBeVisible(); + }); + + test("Team settings has susbscription name and manage subscription link when is owner", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-owner.json", + ); + + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-members?team-id=*", + "subscription/get-team-members-subscription-owner.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-stats?team-id=*", + "dashboard/get-team-stats.json", + ); + + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + + await dashboardPage.goToSecondTeamSettingsSection(); + + await expect(page.getByText("Unlimited (trial)")).toBeVisible(); + await expect( + page.getByRole("button", { name: "Manage your subscription" }), + ).toBeVisible(); + }); + + test("Members tab has warning message when team has more members than subscriptions. Subscribe link is shown for owners.", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-owner.json", + ); + + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-members?team-id=*", + "subscription/get-team-members-subscription-owner.json", + ); + + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + + await dashboardPage.goToSecondTeamMembersSection(); + await expect(page.getByTestId("cta")).toBeVisible(); + await expect(page.getByText("Subscribe now.")).toBeVisible(); + }); + + test("Members tab has warning message when team has more members than subscriptions. Contact to owner is shown for members.", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "logged-in-user/get-profile-logged-in.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-member.json", + ); + + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-members?team-id=*", + "subscription/get-team-members-subscription-member.json", + ); + + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + + await dashboardPage.goToSecondTeamMembersSection(); + await expect(page.getByTestId("cta")).toBeVisible(); + await expect(page.getByText("Contact with the team owner")).toBeVisible(); + }); + + test("Members tab has warning message when has professional subscription and more than 8 members.", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "logged-in-user/get-profile-logged-in.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-professional-subscription-owner.json", + ); + + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-members?team-id=*", + "subscription/get-team-members-more-than-8.json", + ); + + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + + await dashboardPage.goToSecondTeamMembersSection(); + await expect(page.getByTestId("cta")).toBeVisible(); + await expect( + page.getByText( + "The Professional plan is designed for teams of up to 8 editors (owner, admin, and editor).", + ), + ).toBeVisible(); + }); + + test("Invitations tab has warning message when subscription is expired", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-expired-owner.json", + ); + + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-members?team-id=*", + "subscription/get-team-members-subscription-owner.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-invitations?team-id=*", + "dashboard/get-team-invitations-empty.json", + ); + + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + + await dashboardPage.goToSecondTeamInvitationsSection(); + await expect(page.getByTestId("cta")).toBeVisible(); + await expect( + page.getByText( + "Looks like your team has grown! Your plan includes seats, but you're now using more than that.", + ), + ).toBeVisible(); + }); + + test("Invitations tab has warning message when has professional subscription and more than 8 members.", async ({ + page, + }) => { + await DashboardPage.init(page); + await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]); + await DashboardPage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-expired-owner.json", + ); + + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-members?team-id=*", + "subscription/get-team-members-more-than-8.json", + ); + + await DashboardPage.mockRPC( + page, + "get-team-invitations?team-id=*", + "dashboard/get-team-invitations-empty.json", + ); + + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + + await dashboardPage.goToSecondTeamInvitationsSection(); + await expect(page.getByTestId("cta")).toBeVisible(); + await expect( + page.getByText( + "Looks like your team has grown! Your plan includes seats, but you're now using more than that.", + ), + ).toBeVisible(); + }); +}); + +test.describe("Subscriptions: workspace", () => { + test("Unlimited team should have 'Power up your plan' link in main menu", async ({ + page, + }) => { + await WorkspacePage.init(page); + await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]); + + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + + await WorkspacePage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + await workspacePage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + await workspacePage.goToWorkspace(); + await page.getByRole("button", { name: "Main menu" }).click(); + + await expect(page.getByText("Power up your plan")).toBeVisible(); + }); + + test("Enterprise team should not have 'Power up your plan' link in main menu", async ({ + page, + }) => { + await WorkspacePage.init(page); + await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]); + + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + + await WorkspacePage.mockRPC( + page, + "get-profile", + "subscription/get-profile-enterprise-subscription.json", + ); + + await workspacePage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + await workspacePage.goToWorkspace(); + await page.getByRole("button", { name: "Main menu" }).click(); + + await expect(page.getByText("Power up your plan")).not.toBeVisible(); + }); + + test("Professional team should have 7 days autosaved versions", async ({ + page, + }) => { + await WorkspacePage.init(page); + await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]); + + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + + await workspacePage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + await workspacePage.goToWorkspace(); + + await workspacePage.mockRPC( + "get-file-snapshots?file-id=*", + "workspace/versions-snapshot-1.json", + ); + + await page.getByLabel("History").click(); + + await expect( + page.getByText("Autosaved versions will be kept for 7 days."), + ).toBeVisible(); + }); + + test("Unlimited team should have 30 days autosaved versions", async ({ + page, + }) => { + await WorkspacePage.init(page); + await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]); + + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + + await WorkspacePage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + await WorkspacePage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-one-team.json", + ); + + await workspacePage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + await workspacePage.goToWorkspace(); + + await workspacePage.mockRPC( + "get-file-snapshots?file-id=*", + "workspace/versions-snapshot-1.json", + ); + + await page.getByLabel("History").click(); + + await expect( + page.getByText("Autosaved versions will be kept for 30 days."), + ).toBeVisible(); + }); + + test("Unlimited team should have 90 days autosaved versions", async ({ + page, + }) => { + await WorkspacePage.init(page); + await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]); + + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + + await WorkspacePage.mockRPC( + page, + "get-profile", + "subscription/get-profile-enterprise-subscription.json", + ); + + await WorkspacePage.mockRPC( + page, + "get-teams", + "subscription/get-teams-enterprise-one-team.json", + ); + + await workspacePage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + await workspacePage.goToWorkspace(); + + await workspacePage.mockRPC( + "get-file-snapshots?file-id=*", + "workspace/versions-snapshot-1.json", + ); + + await page.getByLabel("History").click(); + + await expect( + page.getByText("Autosaved versions will be kept for 90 days."), + ).toBeVisible(); + }); +}); + +test.describe("Subscriptions: profile", () => { + test("When subscription is professional there is no manage subscription link", async ({ + page, + }) => { + await SubscriptionProfilePage.init(page); + await SubscriptionProfilePage.mockConfigFlags(page, ["enable-subscriptions"]); + + await SubscriptionProfilePage.mockRPC( + page, + "get-profile", + "logged-in-user/get-profile-logged-in.json", + ); + + const subscriptionProfilePage = new SubscriptionProfilePage(page); + + await subscriptionProfilePage.goToSubscriptions(); + + await expect( + page.getByRole("button", { name: "Manage your subscription" }), + ).not.toBeVisible(); + + await expect( + page.getByRole("heading", { name: "Other Penpot plans", level: 3 }), + ).toBeVisible(); + + await expect(page.getByText("$7")).toBeVisible(); + + await expect(page.getByText("$950")).toBeVisible(); + + await expect( + page.getByRole("button", { name: "Try it free for 14 days" }).first(), + ).toBeVisible(); + }); + + test("When subscription is unlimited there is manage subscription link", async ({ + page, + }) => { + await SubscriptionProfilePage.init(page); + await SubscriptionProfilePage.mockConfigFlags(page, ["enable-subscriptions"]); + + await SubscriptionProfilePage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + + const subscriptionProfilePage = new SubscriptionProfilePage(page); + + await subscriptionProfilePage.goToSubscriptions(); + + await expect( + page.getByRole("button", { name: "Manage your subscription" }), + ).toBeVisible(); + + await expect( + page.getByRole("heading", { name: "Other Penpot plans", level: 3 }), + ).toBeVisible(); + + await expect(page.getByText("$0")).toBeVisible(); + + await expect(page.getByText("$950")).toBeVisible(); + + await expect( + page.getByRole("button", { name: "Try it free for 14 days" }).first(), + ).not.toBeVisible(); + + await expect( + page.getByRole("button", { name: "Subscribe" }).first(), + ).toBeVisible(); + }); + + test("When subscription is enteprise there is manage subscription link", async ({ + page, + }) => { + await SubscriptionProfilePage.init(page); + await SubscriptionProfilePage.mockConfigFlags(page, ["enable-subscriptions"]); + + await SubscriptionProfilePage.mockRPC( + page, + "get-profile", + "subscription/get-profile-enterprise-subscription.json", + ); + + const subscriptionProfilePage = new SubscriptionProfilePage(page); + + await subscriptionProfilePage.goToSubscriptions(); + + await expect( + page.getByRole("button", { name: "Manage your subscription" }), + ).toBeVisible(); + + await expect( + page.getByRole("heading", { name: "Other Penpot plans", level: 3 }), + ).toBeVisible(); + + await expect(page.getByText("$0")).toBeVisible(); + + await expect(page.getByText("$7")).toBeVisible(); + + await expect( + page.getByRole("button", { name: "Try it free for 14 days" }).first(), + ).not.toBeVisible(); + + await expect( + page.getByRole("button", { name: "Subscribe" }).first(), + ).toBeVisible(); + }); +}); diff --git a/frontend/src/app/main/ui/dashboard/subscription.cljs b/frontend/src/app/main/ui/dashboard/subscription.cljs index e557db53db..62ea6f6f2e 100644 --- a/frontend/src/app/main/ui/dashboard/subscription.cljs +++ b/frontend/src/app/main/ui/dashboard/subscription.cljs @@ -34,7 +34,7 @@ :cta-without-dropdown (not has-dropdown))} [:div {:class (stl/css :content)} [:span {:class (stl/css :cta-title)} top-title] - [:span {:class (stl/css :cta-text)} top-description]] + [:span {:class (stl/css :cta-text) :data-testid "subscription-name"} top-description]] (when has-dropdown [:span {:class (stl/css :icon-dropdown)} i/arrow])] (when (and has-dropdown show-data) @@ -110,12 +110,13 @@ "enterprise" (tr "subscription.settings.enterprise"))] (when (and is-owner (not= subscription-name "professional")) [:button {:class (stl/css :manage-subscription-link) - :on-click go-to-manage-subscription} + :on-click go-to-manage-subscription + :data-testid "manage-subscription-link"} (tr "subscription.settings.manage-your-subscription")])])) (mf/defc menu-team-icon* [{:keys [subscription-name]}] - [:span {:class (stl/css :subscription-icon)} + [:span {:class (stl/css :subscription-icon) :data-testid "subscription-icon"} (case subscription-name "unlimited" i/character-u "enterprise" i/character-e)])