@mattermost/playwright-lib 10.6.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/README.md +214 -0
  2. package/dist/asset/mattermost-icon_128x128.png +0 -0
  3. package/dist/browser_context.d.ts +17 -0
  4. package/dist/browser_context.js +71 -0
  5. package/dist/browser_context.js.map +1 -0
  6. package/dist/constant.d.ts +3 -0
  7. package/dist/constant.js +8 -0
  8. package/dist/constant.js.map +1 -0
  9. package/dist/file.d.ts +42 -0
  10. package/dist/file.js +74 -0
  11. package/dist/file.js.map +1 -0
  12. package/dist/flag.d.ts +7 -0
  13. package/dist/flag.js +76 -0
  14. package/dist/flag.js.map +1 -0
  15. package/dist/global_setup.d.ts +3 -0
  16. package/dist/global_setup.js +173 -0
  17. package/dist/global_setup.js.map +1 -0
  18. package/dist/index.d.ts +9 -0
  19. package/dist/index.js +47 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/mock_browser_api.d.ts +31 -0
  22. package/dist/mock_browser_api.js +71 -0
  23. package/dist/mock_browser_api.js.map +1 -0
  24. package/dist/server/channel.d.ts +12 -0
  25. package/dist/server/channel.js +25 -0
  26. package/dist/server/channel.js.map +1 -0
  27. package/dist/server/client.d.ts +16 -0
  28. package/dist/server/client.js +39 -0
  29. package/dist/server/client.js.map +1 -0
  30. package/dist/server/default_config.d.ts +2 -0
  31. package/dist/server/default_config.js +755 -0
  32. package/dist/server/default_config.js.map +1 -0
  33. package/dist/server/index.d.ts +7 -0
  34. package/dist/server/init.d.ts +23 -0
  35. package/dist/server/init.js +73 -0
  36. package/dist/server/init.js.map +1 -0
  37. package/dist/server/post.d.ts +2 -0
  38. package/dist/server/post.js +31 -0
  39. package/dist/server/post.js.map +1 -0
  40. package/dist/server/team.d.ts +2 -0
  41. package/dist/server/team.js +18 -0
  42. package/dist/server/team.js.map +1 -0
  43. package/dist/server/user.d.ts +3 -0
  44. package/dist/server/user.js +33 -0
  45. package/dist/server/user.js.map +1 -0
  46. package/dist/test_action.d.ts +4 -0
  47. package/dist/test_action.js +24 -0
  48. package/dist/test_action.js.map +1 -0
  49. package/dist/test_config.d.ts +20 -0
  50. package/dist/test_config.js +82 -0
  51. package/dist/test_config.js.map +1 -0
  52. package/dist/test_fixture.d.ts +72 -0
  53. package/dist/test_fixture.js +172 -0
  54. package/dist/test_fixture.js.map +1 -0
  55. package/dist/types.d.ts +91 -0
  56. package/dist/ui/components/channels/app_bar.d.ts +7 -0
  57. package/dist/ui/components/channels/app_bar.js +20 -0
  58. package/dist/ui/components/channels/app_bar.js.map +1 -0
  59. package/dist/ui/components/channels/center_view.d.ts +52 -0
  60. package/dist/ui/components/channels/center_view.js +124 -0
  61. package/dist/ui/components/channels/center_view.js.map +1 -0
  62. package/dist/ui/components/channels/delete_post_confirmation_dialog.d.ts +11 -0
  63. package/dist/ui/components/channels/delete_post_confirmation_dialog.js +35 -0
  64. package/dist/ui/components/channels/delete_post_confirmation_dialog.js.map +1 -0
  65. package/dist/ui/components/channels/delete_post_modal.d.ts +8 -0
  66. package/dist/ui/components/channels/delete_post_modal.js +26 -0
  67. package/dist/ui/components/channels/delete_post_modal.js.map +1 -0
  68. package/dist/ui/components/channels/emoji_gif_picker.d.ts +16 -0
  69. package/dist/ui/components/channels/emoji_gif_picker.js +50 -0
  70. package/dist/ui/components/channels/emoji_gif_picker.js.map +1 -0
  71. package/dist/ui/components/channels/find_channels_modal.d.ts +8 -0
  72. package/dist/ui/components/channels/find_channels_modal.js +22 -0
  73. package/dist/ui/components/channels/find_channels_modal.js.map +1 -0
  74. package/dist/ui/components/channels/generic_confirm_modal.d.ts +16 -0
  75. package/dist/ui/components/channels/generic_confirm_modal.js +40 -0
  76. package/dist/ui/components/channels/generic_confirm_modal.js.map +1 -0
  77. package/dist/ui/components/channels/header.d.ts +6 -0
  78. package/dist/ui/components/channels/header.js +18 -0
  79. package/dist/ui/components/channels/header.js.map +1 -0
  80. package/dist/ui/components/channels/message_priority.d.ts +18 -0
  81. package/dist/ui/components/channels/message_priority.js +66 -0
  82. package/dist/ui/components/channels/message_priority.js.map +1 -0
  83. package/dist/ui/components/channels/post.d.ts +29 -0
  84. package/dist/ui/components/channels/post.js +63 -0
  85. package/dist/ui/components/channels/post.js.map +1 -0
  86. package/dist/ui/components/channels/post_create.d.ts +41 -0
  87. package/dist/ui/components/channels/post_create.js +118 -0
  88. package/dist/ui/components/channels/post_create.js.map +1 -0
  89. package/dist/ui/components/channels/post_dot_menu.d.ts +20 -0
  90. package/dist/ui/components/channels/post_dot_menu.js +47 -0
  91. package/dist/ui/components/channels/post_dot_menu.js.map +1 -0
  92. package/dist/ui/components/channels/post_edit.d.ts +21 -0
  93. package/dist/ui/components/channels/post_edit.js +76 -0
  94. package/dist/ui/components/channels/post_edit.js.map +1 -0
  95. package/dist/ui/components/channels/post_menu.d.ts +26 -0
  96. package/dist/ui/components/channels/post_menu.js +54 -0
  97. package/dist/ui/components/channels/post_menu.js.map +1 -0
  98. package/dist/ui/components/channels/post_reminder_menu.d.ts +11 -0
  99. package/dist/ui/components/channels/post_reminder_menu.js +29 -0
  100. package/dist/ui/components/channels/post_reminder_menu.js.map +1 -0
  101. package/dist/ui/components/channels/restore_post_confirmation_dialog.d.ts +10 -0
  102. package/dist/ui/components/channels/restore_post_confirmation_dialog.js +30 -0
  103. package/dist/ui/components/channels/restore_post_confirmation_dialog.js.map +1 -0
  104. package/dist/ui/components/channels/scheduled_draft_menu.d.ts +8 -0
  105. package/dist/ui/components/channels/scheduled_draft_menu.js +23 -0
  106. package/dist/ui/components/channels/scheduled_draft_menu.js.map +1 -0
  107. package/dist/ui/components/channels/scheduled_draft_modal.d.ts +20 -0
  108. package/dist/ui/components/channels/scheduled_draft_modal.js +84 -0
  109. package/dist/ui/components/channels/scheduled_draft_modal.js.map +1 -0
  110. package/dist/ui/components/channels/search_popover.d.ts +15 -0
  111. package/dist/ui/components/channels/search_popover.js +43 -0
  112. package/dist/ui/components/channels/search_popover.js.map +1 -0
  113. package/dist/ui/components/channels/settings/notification_settings.d.ts +13 -0
  114. package/dist/ui/components/channels/settings/notification_settings.js +41 -0
  115. package/dist/ui/components/channels/settings/notification_settings.js.map +1 -0
  116. package/dist/ui/components/channels/settings/settings_modal.d.ts +11 -0
  117. package/dist/ui/components/channels/settings/settings_modal.js +32 -0
  118. package/dist/ui/components/channels/settings/settings_modal.js.map +1 -0
  119. package/dist/ui/components/channels/sidebar_left.d.ts +24 -0
  120. package/dist/ui/components/channels/sidebar_left.js +54 -0
  121. package/dist/ui/components/channels/sidebar_left.js.map +1 -0
  122. package/dist/ui/components/channels/sidebar_right.d.ts +38 -0
  123. package/dist/ui/components/channels/sidebar_right.js +87 -0
  124. package/dist/ui/components/channels/sidebar_right.js.map +1 -0
  125. package/dist/ui/components/channels/thread_footer.d.ts +11 -0
  126. package/dist/ui/components/channels/thread_footer.js +27 -0
  127. package/dist/ui/components/channels/thread_footer.js.map +1 -0
  128. package/dist/ui/components/channels/user_profile_popover.d.ts +7 -0
  129. package/dist/ui/components/channels/user_profile_popover.js +21 -0
  130. package/dist/ui/components/channels/user_profile_popover.js.map +1 -0
  131. package/dist/ui/components/footer.d.ts +11 -0
  132. package/dist/ui/components/footer.js +28 -0
  133. package/dist/ui/components/footer.js.map +1 -0
  134. package/dist/ui/components/global_header.d.ts +15 -0
  135. package/dist/ui/components/global_header.js +46 -0
  136. package/dist/ui/components/global_header.js.map +1 -0
  137. package/dist/ui/components/index.d.ts +73 -0
  138. package/dist/ui/components/index.js +77 -0
  139. package/dist/ui/components/index.js.map +1 -0
  140. package/dist/ui/components/main_header.d.ts +8 -0
  141. package/dist/ui/components/main_header.js +22 -0
  142. package/dist/ui/components/main_header.js.map +1 -0
  143. package/dist/ui/components/system_console/navbar.d.ts +6 -0
  144. package/dist/ui/components/system_console/navbar.js +18 -0
  145. package/dist/ui/components/system_console/navbar.js.map +1 -0
  146. package/dist/ui/components/system_console/sections/system_users/column_toggle_menu.d.ts +22 -0
  147. package/dist/ui/components/system_console/sections/system_users/column_toggle_menu.js +47 -0
  148. package/dist/ui/components/system_console/sections/system_users/column_toggle_menu.js.map +1 -0
  149. package/dist/ui/components/system_console/sections/system_users/feature_discovery.d.ts +10 -0
  150. package/dist/ui/components/system_console/sections/system_users/feature_discovery.js +24 -0
  151. package/dist/ui/components/system_console/sections/system_users/feature_discovery.js.map +1 -0
  152. package/dist/ui/components/system_console/sections/system_users/filter_menu.d.ts +21 -0
  153. package/dist/ui/components/system_console/sections/system_users/filter_menu.js +42 -0
  154. package/dist/ui/components/system_console/sections/system_users/filter_menu.js.map +1 -0
  155. package/dist/ui/components/system_console/sections/system_users/filter_popover.d.ts +30 -0
  156. package/dist/ui/components/system_console/sections/system_users/filter_popover.js +61 -0
  157. package/dist/ui/components/system_console/sections/system_users/filter_popover.js.map +1 -0
  158. package/dist/ui/components/system_console/sections/system_users/mobile_security.d.ts +23 -0
  159. package/dist/ui/components/system_console/sections/system_users/mobile_security.js +56 -0
  160. package/dist/ui/components/system_console/sections/system_users/mobile_security.js.map +1 -0
  161. package/dist/ui/components/system_console/sections/system_users/system_users.d.ts +58 -0
  162. package/dist/ui/components/system_console/sections/system_users/system_users.js +114 -0
  163. package/dist/ui/components/system_console/sections/system_users/system_users.js.map +1 -0
  164. package/dist/ui/components/system_console/sidebar.d.ts +17 -0
  165. package/dist/ui/components/system_console/sidebar.js +37 -0
  166. package/dist/ui/components/system_console/sidebar.js.map +1 -0
  167. package/dist/ui/pages/channels.d.ts +31 -0
  168. package/dist/ui/pages/channels.js +79 -0
  169. package/dist/ui/pages/channels.js.map +1 -0
  170. package/dist/ui/pages/drafts.d.ts +30 -0
  171. package/dist/ui/pages/drafts.js +114 -0
  172. package/dist/ui/pages/drafts.js.map +1 -0
  173. package/dist/ui/pages/index.d.ts +19 -0
  174. package/dist/ui/pages/index.js +34 -0
  175. package/dist/ui/pages/index.js.map +1 -0
  176. package/dist/ui/pages/landing_login.d.ts +11 -0
  177. package/dist/ui/pages/landing_login.js +36 -0
  178. package/dist/ui/pages/landing_login.js.map +1 -0
  179. package/dist/ui/pages/login.d.ts +25 -0
  180. package/dist/ui/pages/login.js +62 -0
  181. package/dist/ui/pages/login.js.map +1 -0
  182. package/dist/ui/pages/reset_password.d.ts +15 -0
  183. package/dist/ui/pages/reset_password.js +44 -0
  184. package/dist/ui/pages/reset_password.js.map +1 -0
  185. package/dist/ui/pages/scheduled_draft.d.ts +38 -0
  186. package/dist/ui/pages/scheduled_draft.js +144 -0
  187. package/dist/ui/pages/scheduled_draft.js.map +1 -0
  188. package/dist/ui/pages/signup.d.ts +31 -0
  189. package/dist/ui/pages/signup.js +78 -0
  190. package/dist/ui/pages/signup.js.map +1 -0
  191. package/dist/ui/pages/system_console.d.ts +26 -0
  192. package/dist/ui/pages/system_console.js +64 -0
  193. package/dist/ui/pages/system_console.js.map +1 -0
  194. package/dist/util.d.ts +29 -0
  195. package/dist/util.js +51 -0
  196. package/dist/util.js.map +1 -0
  197. package/dist/visual/index.d.ts +3 -0
  198. package/dist/visual/index.js +37 -0
  199. package/dist/visual/index.js.map +1 -0
  200. package/dist/visual/percy.d.ts +2 -0
  201. package/dist/visual/percy.js +23 -0
  202. package/dist/visual/percy.js.map +1 -0
  203. package/package.json +68 -0
package/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # @mattermost/playwright-lib
2
+
3
+ A comprehensive end-to-end testing library for Mattermost web, desktop and plugin applications using Playwright.
4
+
5
+ ## Overview
6
+
7
+ This library provides:
8
+
9
+ - Pre-built page objects and components for common Mattermost UI elements
10
+ - Server configuration and initialization utilities
11
+ - Test fixtures and helpers
12
+ - Visual testing support with Percy integration
13
+ - Accessibility testing support with [axe-core](https://github.com/dequelabs/axe-core)
14
+ - Browser notification mocking
15
+ - File handling utilities
16
+ - Common test actions and assertions
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @mattermost/playwright-lib
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Basic example of logging in and posting a message:
27
+
28
+ ```typescript
29
+ import {test, expect} from '@mattermost/playwright-lib';
30
+
31
+ test('user can post message', async ({pw}) => {
32
+ // # Create and login a new user
33
+ const {user} = await pw.initSetup();
34
+ const {channelsPage} = await pw.testBrowser.login(user);
35
+
36
+ // # Navigate and post a message
37
+ await channelsPage.goto();
38
+ const message = 'Hello World!';
39
+ await channelsPage.postMessage(message);
40
+
41
+ // * Verify message appears
42
+ const lastPost = await channelsPage.getLastPost();
43
+ await expect(lastPost).toHaveText(message);
44
+ });
45
+ ```
46
+
47
+ ## Key Components
48
+
49
+ ### Page Objects
50
+
51
+ Ready-to-use page objects for common Mattermost pages:
52
+
53
+ - Login
54
+ - Signup
55
+ - Channels
56
+ - System Console
57
+ - And more...
58
+
59
+ ### UI Components
60
+
61
+ Reusable component objects for UI elements:
62
+
63
+ - Headers
64
+ - Posts
65
+ - Menus
66
+ - Modals
67
+ - And more...
68
+
69
+ ### Test Utilities
70
+
71
+ Helper functions for common testing needs:
72
+
73
+ - Server setup and configuration
74
+ - User/team creation
75
+ - File handling
76
+ - Visual testing
77
+ - And more...
78
+
79
+ ## Configuration
80
+
81
+ The library can be configured via optional environment variables:
82
+
83
+ ### Environment Variables
84
+
85
+ All environment variables are optional with sensible defaults.
86
+
87
+ #### Server Configuration
88
+
89
+ | Variable | Description | Default |
90
+ | ----------------------------- | ------------------------------------------ | -------------------------------- |
91
+ | `PW_BASE_URL` | Server URL | `http://localhost:8065` |
92
+ | `PW_ADMIN_USERNAME` | Admin username | `sysadmin` |
93
+ | `PW_ADMIN_PASSWORD` | Admin password | `Sys@dmin-sample1` |
94
+ | `PW_ADMIN_EMAIL` | Admin email | `sysadmin@sample.mattermost.com` |
95
+ | `PW_ENSURE_PLUGINS_INSTALLED` | Comma-separated list of plugins to install | `[]` |
96
+ | `PW_RESET_BEFORE_TEST` | Reset server before test | `false` |
97
+
98
+ #### High Availability Cluster Settings
99
+
100
+ | Variable | Description | Default |
101
+ | -------------------------- | ----------------------- | ---------------- |
102
+ | `PW_HA_CLUSTER_ENABLED` | Enable HA cluster | `false` |
103
+ | `PW_HA_CLUSTER_NODE_COUNT` | Number of cluster nodes | `2` |
104
+ | `PW_HA_CLUSTER_NAME` | Cluster name | `mm_dev_cluster` |
105
+
106
+ #### Push Notifications
107
+
108
+ | Variable | Description | Default |
109
+ | ----------------------------- | ---------------------------- | ---------------------------------- |
110
+ | `PW_PUSH_NOTIFICATION_SERVER` | Push notification server URL | `https://push-test.mattermost.com` |
111
+
112
+ #### Playwright Settings
113
+
114
+ | Variable | Description | Default |
115
+ | ------------- | ------------------------------- | ------- |
116
+ | `PW_HEADLESS` | Run tests headless | `true` |
117
+ | `PW_SLOWMO` | Add delay between actions in ms | `0` |
118
+ | `PW_WORKERS` | Number of parallel workers | `1` |
119
+
120
+ #### Visual Testing
121
+
122
+ | Variable | Description | Default |
123
+ | -------------------- | --------------------------- | ------- |
124
+ | `PW_SNAPSHOT_ENABLE` | Enable snapshot testing | `false` |
125
+ | `PW_PERCY_ENABLE` | Enable Percy visual testing | `false` |
126
+
127
+ #### CI Settings
128
+
129
+ | Variable | Description | Default |
130
+ | -------- | ------------------------------------ | ------- |
131
+ | `CI` | Set automatically in CI environments | N/A |
132
+
133
+ ## Accessibility Testing
134
+
135
+ The library includes built-in accessibility testing using [axe-core](https://github.com/dequelabs/axe-core):
136
+
137
+ ```typescript
138
+ import {test, expect} from '@mattermost/playwright-lib';
139
+
140
+ test('verify login page accessibility', async ({page, axe}) => {
141
+ // # Navigate to login page
142
+ await page.goto('/login');
143
+
144
+ // # Run accessibility scan
145
+ const results = await axe.builder(page).analyze();
146
+
147
+ // * Verify no accessibility violations
148
+ expect(results.violations).toHaveLength(0);
149
+ });
150
+ ```
151
+
152
+ The axe-core integration:
153
+
154
+ - Runs WCAG 2.0 Level A & AA rules by default
155
+ - Provides detailed violation reports
156
+ - Supports rule customization
157
+ - Can be configured per-test or globally
158
+
159
+ ## Visual Testing
160
+
161
+ The library supports visual testing through [Playwright's built-in visual comparisons](https://playwright.dev/docs/test-snapshots) and [Percy](https://www.browserstack.com/percy) integration:
162
+
163
+ ```typescript
164
+ import {test, expect} from '@mattermost/playwright-lib';
165
+
166
+ test('verify channel header appearance', async ({pw, browserName, viewport}, testInfo) => {
167
+ // # Setup and login
168
+ const {user} = await pw.initSetup();
169
+ const {page, channelsPage} = await pw.testBrowser.login(user);
170
+
171
+ // # Navigate and prepare page
172
+ await channelsPage.goto();
173
+ await expect(channelsPage.appBar.playbooksIcon).toBeVisible();
174
+ await pw.hideDynamicChannelsContent(page);
175
+
176
+ // * Take and verify snapshot
177
+ await pw.matchSnapshot(testInfo, {page, browserName, viewport});
178
+ });
179
+ ```
180
+
181
+ ## Browser Notifications
182
+
183
+ Mock and verify browser notifications:
184
+
185
+ ```typescript
186
+ import {test, expect} from '@mattermost/playwright-lib';
187
+
188
+ test('verify notification on mention', async ({pw}) => {
189
+ // # Setup users and team
190
+ const {team, adminUser, user} = await pw.initSetup();
191
+
192
+ // # Setup admin browser with notifications
193
+ const {page: adminPage, channelsPage: adminChannelsPage} = await pw.testBrowser.login(adminUser);
194
+ await adminChannelsPage.goto(team.name, 'town-square');
195
+ await pw.stubNotification(adminPage, 'granted');
196
+
197
+ // # Setup user browser and post mention
198
+ const {channelsPage: userChannelsPage} = await pw.testBrowser.login(user);
199
+ await userChannelsPage.goto(team.name, 'off-topic');
200
+ await userChannelsPage.postMessage(`@ALL good morning, ${team.name}!`);
201
+
202
+ // * Verify notification received
203
+ const notifications = await pw.waitForNotification(adminPage);
204
+ expect(notifications.length).toBe(1);
205
+ });
206
+ ```
207
+
208
+ ## Contributing
209
+
210
+ See [CONTRIBUTING.md](../../../CONTRIBUTING.md) for development setup and guidelines.
211
+
212
+ ## License
213
+
214
+ See [LICENSE.txt](../../../LICENSE.txt) for license information.
@@ -0,0 +1,17 @@
1
+ import { Browser, BrowserContext } from '@playwright/test';
2
+ import { UserProfile } from '@mattermost/types/users';
3
+ export declare class TestBrowser {
4
+ readonly browser: Browser;
5
+ context: BrowserContext | null;
6
+ constructor(browser: Browser);
7
+ login(user: UserProfile): Promise<{
8
+ context: BrowserContext;
9
+ page: import("playwright-core").Page;
10
+ channelsPage: import("./ui/pages").ChannelsPage;
11
+ systemConsolePage: import("./ui/pages").SystemConsolePage;
12
+ scheduledDraftPage: import("./ui/pages").ScheduledDraftPage;
13
+ draftPage: import("./ui/pages").DraftPage;
14
+ }>;
15
+ close(): Promise<void>;
16
+ }
17
+ export declare function loginByAPI(loginId: string, password: string, token?: string, ldapOnly?: boolean): Promise<string>;
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ var promises = require('node:fs/promises');
4
+ var test = require('@playwright/test');
5
+ var test_config = require('./test_config.js');
6
+ var index = require('./ui/pages/index.js');
7
+
8
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
9
+ // See LICENSE.txt for license information.
10
+ class TestBrowser {
11
+ browser;
12
+ context;
13
+ constructor(browser) {
14
+ this.browser = browser;
15
+ this.context = null;
16
+ }
17
+ async login(user) {
18
+ const options = { storageState: '' };
19
+ if (user) {
20
+ // Log in via API request and save user storage
21
+ const storagePath = await loginByAPI(user.username, user.password);
22
+ options.storageState = storagePath;
23
+ }
24
+ // Sign in a user in new browser context
25
+ const context = await this.browser.newContext(options);
26
+ const page = await context.newPage();
27
+ const channelsPage = new index.pages.ChannelsPage(page);
28
+ const systemConsolePage = new index.pages.SystemConsolePage(page);
29
+ const scheduledDraftPage = new index.pages.ScheduledDraftPage(page);
30
+ const draftPage = new index.pages.DraftPage(page);
31
+ this.context = context;
32
+ return { context, page, channelsPage, systemConsolePage, scheduledDraftPage, draftPage };
33
+ }
34
+ async close() {
35
+ if (this.context) {
36
+ await this.context.close();
37
+ }
38
+ }
39
+ }
40
+ async function loginByAPI(loginId, password, token = '', ldapOnly = false) {
41
+ const requestContext = await test.request.newContext();
42
+ const data = {
43
+ login_id: loginId,
44
+ password,
45
+ token,
46
+ deviceId: '',
47
+ };
48
+ if (ldapOnly) {
49
+ data.ldap_only = 'true';
50
+ }
51
+ // Log in via API
52
+ await requestContext.post(`${test_config.testConfig.baseURL}/api/v4/users/login`, {
53
+ data,
54
+ headers: { 'X-Requested-With': 'XMLHttpRequest' },
55
+ });
56
+ // Save signed-in state to a folder
57
+ const storagePath = `storage_state/${Date.now()}_${loginId}_${password}${token ? '_' + token : ''}${ldapOnly ? '_ldap' : ''}.json`;
58
+ const storageState = await requestContext.storageState({ path: storagePath });
59
+ await requestContext.dispose();
60
+ // Append origins to bypass seeing landing page then write to file
61
+ storageState.origins.push({
62
+ origin: test_config.testConfig.baseURL,
63
+ localStorage: [{ name: '__landingPageSeen__', value: 'true' }],
64
+ });
65
+ await promises.writeFile(storagePath, JSON.stringify(storageState));
66
+ return storagePath;
67
+ }
68
+
69
+ exports.TestBrowser = TestBrowser;
70
+ exports.loginByAPI = loginByAPI;
71
+ //# sourceMappingURL=browser_context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser_context.js","sources":["../src/browser_context.ts"],"sourcesContent":["// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n// See LICENSE.txt for license information.\n\nimport {writeFile} from 'node:fs/promises';\n\nimport {Browser, BrowserContext, request} from '@playwright/test';\nimport {UserProfile} from '@mattermost/types/users';\n\nimport {testConfig} from './test_config';\nimport {pages} from './ui/pages';\n\nexport class TestBrowser {\n readonly browser: Browser;\n context: BrowserContext | null;\n\n constructor(browser: Browser) {\n this.browser = browser;\n this.context = null;\n }\n\n async login(user: UserProfile) {\n const options = {storageState: ''};\n if (user) {\n // Log in via API request and save user storage\n const storagePath = await loginByAPI(user.username, user.password);\n options.storageState = storagePath;\n }\n\n // Sign in a user in new browser context\n const context = await this.browser.newContext(options);\n const page = await context.newPage();\n\n const channelsPage = new pages.ChannelsPage(page);\n const systemConsolePage = new pages.SystemConsolePage(page);\n const scheduledDraftPage = new pages.ScheduledDraftPage(page);\n const draftPage = new pages.DraftPage(page);\n\n this.context = context;\n\n return {context, page, channelsPage, systemConsolePage, scheduledDraftPage, draftPage};\n }\n\n async close() {\n if (this.context) {\n await this.context.close();\n }\n }\n}\n\nexport async function loginByAPI(loginId: string, password: string, token = '', ldapOnly = false) {\n const requestContext = await request.newContext();\n\n const data: any = {\n login_id: loginId,\n password,\n token,\n deviceId: '',\n };\n\n if (ldapOnly) {\n data.ldap_only = 'true';\n }\n\n // Log in via API\n await requestContext.post(`${testConfig.baseURL}/api/v4/users/login`, {\n data,\n headers: {'X-Requested-With': 'XMLHttpRequest'},\n });\n\n // Save signed-in state to a folder\n const storagePath = `storage_state/${Date.now()}_${loginId}_${password}${token ? '_' + token : ''}${\n ldapOnly ? '_ldap' : ''\n }.json`;\n const storageState = await requestContext.storageState({path: storagePath});\n await requestContext.dispose();\n\n // Append origins to bypass seeing landing page then write to file\n storageState.origins.push({\n origin: testConfig.baseURL,\n localStorage: [{name: '__landingPageSeen__', value: 'true'}],\n });\n await writeFile(storagePath, JSON.stringify(storageState));\n\n return storagePath;\n}\n"],"names":["pages","request","testConfig","writeFile"],"mappings":";;;;;;;AAAA;AACA;MAUa,WAAW,CAAA;AACX,IAAA,OAAO;AAChB,IAAA,OAAO;AAEP,IAAA,WAAA,CAAY,OAAgB,EAAA;AACxB,QAAA,IAAI,CAAC,OAAO,GAAG,OAAO;AACtB,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI;;IAGvB,MAAM,KAAK,CAAC,IAAiB,EAAA;AACzB,QAAA,MAAM,OAAO,GAAG,EAAC,YAAY,EAAE,EAAE,EAAC;QAClC,IAAI,IAAI,EAAE;;AAEN,YAAA,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC;AAClE,YAAA,OAAO,CAAC,YAAY,GAAG,WAAW;;;QAItC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;AACtD,QAAA,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE;QAEpC,MAAM,YAAY,GAAG,IAAIA,WAAK,CAAC,YAAY,CAAC,IAAI,CAAC;QACjD,MAAM,iBAAiB,GAAG,IAAIA,WAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC;QAC3D,MAAM,kBAAkB,GAAG,IAAIA,WAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC;QAC7D,MAAM,SAAS,GAAG,IAAIA,WAAK,CAAC,SAAS,CAAC,IAAI,CAAC;AAE3C,QAAA,IAAI,CAAC,OAAO,GAAG,OAAO;AAEtB,QAAA,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,SAAS,EAAC;;AAG1F,IAAA,MAAM,KAAK,GAAA;AACP,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE;AACd,YAAA,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;;;AAGrC;AAEM,eAAe,UAAU,CAAC,OAAe,EAAE,QAAgB,EAAE,KAAK,GAAG,EAAE,EAAE,QAAQ,GAAG,KAAK,EAAA;AAC5F,IAAA,MAAM,cAAc,GAAG,MAAMC,YAAO,CAAC,UAAU,EAAE;AAEjD,IAAA,MAAM,IAAI,GAAQ;AACd,QAAA,QAAQ,EAAE,OAAO;QACjB,QAAQ;QACR,KAAK;AACL,QAAA,QAAQ,EAAE,EAAE;KACf;IAED,IAAI,QAAQ,EAAE;AACV,QAAA,IAAI,CAAC,SAAS,GAAG,MAAM;;;IAI3B,MAAM,cAAc,CAAC,IAAI,CAAC,GAAGC,sBAAU,CAAC,OAAO,CAAA,mBAAA,CAAqB,EAAE;QAClE,IAAI;AACJ,QAAA,OAAO,EAAE,EAAC,kBAAkB,EAAE,gBAAgB,EAAC;AAClD,KAAA,CAAC;;AAGF,IAAA,MAAM,WAAW,GAAG,CAAiB,cAAA,EAAA,IAAI,CAAC,GAAG,EAAE,CAAI,CAAA,EAAA,OAAO,IAAI,QAAQ,CAAA,EAAG,KAAK,GAAG,GAAG,GAAG,KAAK,GAAG,EAAE,GAC7F,QAAQ,GAAG,OAAO,GAAG,EACzB,OAAO;AACP,IAAA,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,YAAY,CAAC,EAAC,IAAI,EAAE,WAAW,EAAC,CAAC;AAC3E,IAAA,MAAM,cAAc,CAAC,OAAO,EAAE;;AAG9B,IAAA,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC;QACtB,MAAM,EAAEA,sBAAU,CAAC,OAAO;QAC1B,YAAY,EAAE,CAAC,EAAC,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,EAAC,CAAC;AAC/D,KAAA,CAAC;IACF,MAAMC,kBAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;AAE1D,IAAA,OAAO,WAAW;AACtB;;;;;"}
@@ -0,0 +1,3 @@
1
+ export declare const appsPluginId = "com.mattermost.apps";
2
+ export declare const callsPluginId = "com.mattermost.calls";
3
+ export declare const playbooksPluginId = "playbooks";
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
4
+ // See LICENSE.txt for license information.
5
+ const callsPluginId = 'com.mattermost.calls';
6
+
7
+ exports.callsPluginId = callsPluginId;
8
+ //# sourceMappingURL=constant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constant.js","sources":["../src/constant.ts"],"sourcesContent":["// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n// See LICENSE.txt for license information.\n\nexport const appsPluginId = 'com.mattermost.apps';\nexport const callsPluginId = 'com.mattermost.calls';\nexport const playbooksPluginId = 'playbooks';\n"],"names":[],"mappings":";;AAAA;AACA;AAGO,MAAM,aAAa,GAAG;;;;"}
package/dist/file.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ export declare const assetPath: string;
2
+ declare const availableFiles: readonly ["mattermost-icon_128x128.png"];
3
+ type AvailableFilename = (typeof availableFiles)[number];
4
+ /**
5
+ * Reads file data and creates a File object.
6
+ * @param filePath - The path to the file.
7
+ * @returns A File object containing the file data.
8
+ * @throws If the file does not exist.
9
+ */
10
+ export declare function getFileData(filePath: string): File;
11
+ /**
12
+ * Reads file data and creates a Blob object.
13
+ * @param filePath - The path to the file.
14
+ * @returns A Blob object containing the file data.
15
+ * @throws If the file does not exist.
16
+ */
17
+ export declare function getBlobData(filePath: string): Blob;
18
+ /**
19
+ * Reads file data from the "asset" directory and creates a File object.
20
+ * @param filename - The name of the file in the "asset" directory.
21
+ * @returns An object containing a File object
22
+ */
23
+ export declare function getFileFromAsset(filename: string): File;
24
+ /**
25
+ * Reads file data from the "asset" directory and creates a Blob object.
26
+ * @param filename - The name of the file in the "asset" directory.
27
+ * @returns An object containing a Blob object
28
+ */
29
+ export declare function getBlobFromAsset(filename: string): Blob;
30
+ /**
31
+ * Reads file data from the lib "asset" directory and creates a File object.
32
+ * @param filename - The name of the file in the "asset" directory.
33
+ * @returns An object containing a File object
34
+ */
35
+ export declare function getFileFromCommonAsset(filename: AvailableFilename): File;
36
+ /**
37
+ * Reads file data from the lib "asset" directory and creates a Blob object.
38
+ * @param filename - The name of the file in the "asset" directory.
39
+ * @returns An object containing a Blob object
40
+ */
41
+ export declare function getBlobFromCommonAsset(filename: AvailableFilename): Blob;
42
+ export {};
package/dist/file.js ADDED
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ var path = require('node:path');
4
+ var fs = require('node:fs');
5
+ var mime = require('mime-types');
6
+
7
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
8
+ // See LICENSE.txt for license information.
9
+ const commonAssetPath = path.resolve(__dirname, 'asset');
10
+ const assetPath = path.resolve(process.cwd(), 'asset');
11
+ /**
12
+ * Reads file data and creates a File object.
13
+ * @param filePath - The path to the file.
14
+ * @returns A File object containing the file data.
15
+ * @throws If the file does not exist.
16
+ */
17
+ function getFileData(filePath) {
18
+ if (!fs.existsSync(filePath)) {
19
+ throw new Error(`File not found at path: ${filePath}`);
20
+ }
21
+ const mimeType = mime.lookup(filePath) || undefined;
22
+ const fileName = path.basename(filePath);
23
+ const fileBuffer = fs.readFileSync(filePath);
24
+ return new File([fileBuffer], fileName, { type: mimeType });
25
+ }
26
+ /**
27
+ * Reads file data and creates a Blob object.
28
+ * @param filePath - The path to the file.
29
+ * @returns A Blob object containing the file data.
30
+ * @throws If the file does not exist.
31
+ */
32
+ function getBlobData(filePath) {
33
+ if (!fs.existsSync(filePath)) {
34
+ throw new Error(`File not found at path: ${filePath}`);
35
+ }
36
+ const mimeType = mime.lookup(filePath) || undefined;
37
+ const fileBuffer = fs.readFileSync(filePath);
38
+ return new Blob([fileBuffer], { type: mimeType });
39
+ }
40
+ /**
41
+ * Reads file data from the "asset" directory and creates a File object.
42
+ * @param filename - The name of the file in the "asset" directory.
43
+ * @returns An object containing a File object
44
+ */
45
+ function getFileFromAsset(filename) {
46
+ const filePath = path.join(assetPath, filename);
47
+ return getFileData(filePath);
48
+ }
49
+ /**
50
+ * Reads file data from the "asset" directory and creates a Blob object.
51
+ * @param filename - The name of the file in the "asset" directory.
52
+ * @returns An object containing a Blob object
53
+ */
54
+ function getBlobFromAsset(filename) {
55
+ const filePath = path.join(assetPath, filename);
56
+ return getBlobData(filePath);
57
+ }
58
+ /**
59
+ * Reads file data from the lib "asset" directory and creates a File object.
60
+ * @param filename - The name of the file in the "asset" directory.
61
+ * @returns An object containing a File object
62
+ */
63
+ function getFileFromCommonAsset(filename) {
64
+ const filePath = path.join(commonAssetPath, filename);
65
+ return getFileData(filePath);
66
+ }
67
+
68
+ exports.assetPath = assetPath;
69
+ exports.getBlobData = getBlobData;
70
+ exports.getBlobFromAsset = getBlobFromAsset;
71
+ exports.getFileData = getFileData;
72
+ exports.getFileFromAsset = getFileFromAsset;
73
+ exports.getFileFromCommonAsset = getFileFromCommonAsset;
74
+ //# sourceMappingURL=file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.js","sources":["../src/file.ts"],"sourcesContent":["// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n// See LICENSE.txt for license information.\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport mime from 'mime-types';\n\nconst commonAssetPath = path.resolve(__dirname, 'asset');\nexport const assetPath = path.resolve(process.cwd(), 'asset');\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst availableFiles = ['mattermost-icon_128x128.png'] as const;\ntype AvailableFilename = (typeof availableFiles)[number];\n\n/**\n * Reads file data and creates a File object.\n * @param filePath - The path to the file.\n * @returns A File object containing the file data.\n * @throws If the file does not exist.\n */\nexport function getFileData(filePath: string): File {\n if (!fs.existsSync(filePath)) {\n throw new Error(`File not found at path: ${filePath}`);\n }\n\n const mimeType = mime.lookup(filePath) || undefined;\n const fileName = path.basename(filePath);\n const fileBuffer = fs.readFileSync(filePath);\n\n return new File([fileBuffer], fileName, {type: mimeType});\n}\n\n/**\n * Reads file data and creates a Blob object.\n * @param filePath - The path to the file.\n * @returns A Blob object containing the file data.\n * @throws If the file does not exist.\n */\nexport function getBlobData(filePath: string): Blob {\n if (!fs.existsSync(filePath)) {\n throw new Error(`File not found at path: ${filePath}`);\n }\n\n const mimeType = mime.lookup(filePath) || undefined;\n const fileBuffer = fs.readFileSync(filePath);\n\n return new Blob([fileBuffer], {type: mimeType});\n}\n\n/**\n * Reads file data from the \"asset\" directory and creates a File object.\n * @param filename - The name of the file in the \"asset\" directory.\n * @returns An object containing a File object\n */\nexport function getFileFromAsset(filename: string) {\n const filePath = path.join(assetPath, filename);\n\n return getFileData(filePath);\n}\n\n/**\n * Reads file data from the \"asset\" directory and creates a Blob object.\n * @param filename - The name of the file in the \"asset\" directory.\n * @returns An object containing a Blob object\n */\nexport function getBlobFromAsset(filename: string) {\n const filePath = path.join(assetPath, filename);\n\n return getBlobData(filePath);\n}\n\n/**\n * Reads file data from the lib \"asset\" directory and creates a File object.\n * @param filename - The name of the file in the \"asset\" directory.\n * @returns An object containing a File object\n */\nexport function getFileFromCommonAsset(filename: AvailableFilename) {\n const filePath = path.join(commonAssetPath, filename);\n\n return getFileData(filePath);\n}\n\n/**\n * Reads file data from the lib \"asset\" directory and creates a Blob object.\n * @param filename - The name of the file in the \"asset\" directory.\n * @returns An object containing a Blob object\n */\nexport function getBlobFromCommonAsset(filename: AvailableFilename) {\n const filePath = path.join(commonAssetPath, filename);\n\n return getBlobData(filePath);\n}\n"],"names":[],"mappings":";;;;;;AAAA;AACA;AAOA,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC;AACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO;AAM5D;;;;;AAKG;AACG,SAAU,WAAW,CAAC,QAAgB,EAAA;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;AAC1B,QAAA,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,CAAA,CAAE,CAAC;;IAG1D,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,SAAS;IACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACxC,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC;AAE5C,IAAA,OAAO,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC,CAAC;AAC7D;AAEA;;;;;AAKG;AACG,SAAU,WAAW,CAAC,QAAgB,EAAA;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;AAC1B,QAAA,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,CAAA,CAAE,CAAC;;IAG1D,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,SAAS;IACnD,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC;AAE5C,IAAA,OAAO,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC,CAAC;AACnD;AAEA;;;;AAIG;AACG,SAAU,gBAAgB,CAAC,QAAgB,EAAA;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC;AAE/C,IAAA,OAAO,WAAW,CAAC,QAAQ,CAAC;AAChC;AAEA;;;;AAIG;AACG,SAAU,gBAAgB,CAAC,QAAgB,EAAA;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC;AAE/C,IAAA,OAAO,WAAW,CAAC,QAAQ,CAAC;AAChC;AAEA;;;;AAIG;AACG,SAAU,sBAAsB,CAAC,QAA2B,EAAA;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC;AAErD,IAAA,OAAO,WAAW,CAAC,QAAQ,CAAC;AAChC;;;;;;;;;"}
package/dist/flag.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export declare function shouldHaveCallsEnabled(enabled?: boolean): Promise<void>;
2
+ export declare function shouldHaveFeatureFlag(name: string, value: string | boolean): Promise<void>;
3
+ export declare function shouldRunInLinux(): Promise<void>;
4
+ export declare function ensureLicense(): Promise<void>;
5
+ export declare function requestTrialLicense(): Promise<void>;
6
+ export declare function skipIfNoLicense(): Promise<void>;
7
+ export declare function skipIfFeatureFlagNotSet(name: string, value: string | boolean): Promise<void>;
package/dist/flag.js ADDED
@@ -0,0 +1,76 @@
1
+ 'use strict';
2
+
3
+ var os = require('node:os');
4
+ var test = require('@playwright/test');
5
+ var constant = require('./constant.js');
6
+ var init = require('./server/init.js');
7
+
8
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
9
+ // See LICENSE.txt for license information.
10
+ async function shouldHaveCallsEnabled(enabled = true) {
11
+ const { adminClient } = await init.getAdminClient();
12
+ const config = await adminClient.getConfig();
13
+ const callsEnabled = config.PluginSettings.PluginStates[constant.callsPluginId].Enable;
14
+ const matched = callsEnabled === enabled;
15
+ test.expect(matched, matched ? '' : `Calls expect "${enabled}" but actual "${callsEnabled}"`).toBeTruthy();
16
+ }
17
+ async function shouldHaveFeatureFlag(name, value) {
18
+ const { adminClient } = await init.getAdminClient();
19
+ const config = await adminClient.getConfig();
20
+ const matched = config.FeatureFlags[name] === value;
21
+ test.expect(matched, matched ? '' : `FeatureFlags["${name}'] expect "${value}" but actual "${config.FeatureFlags[name]}"`).toBeTruthy();
22
+ }
23
+ async function shouldRunInLinux() {
24
+ const platform = os.platform();
25
+ test.expect(platform, 'Run in Linux or Playwright docker image only').toBe('linux');
26
+ }
27
+ async function ensureLicense() {
28
+ const { adminClient } = await init.getAdminClient();
29
+ let license = await adminClient.getClientLicenseOld();
30
+ if (license?.IsLicensed !== 'true') {
31
+ const config = await adminClient.getClientConfigOld();
32
+ test.expect(config.ServiceEnvironment === 'dev', 'The trial license request fails in the local development environment. Please manually upload the test license.').toBeFalsy();
33
+ await requestTrialLicense();
34
+ license = await adminClient.getClientLicenseOld();
35
+ }
36
+ test.expect(license?.IsLicensed === 'true', 'Ensure server has license').toBeTruthy();
37
+ }
38
+ async function requestTrialLicense() {
39
+ const { adminClient } = await init.getAdminClient();
40
+ const admin = await adminClient.getMe();
41
+ try {
42
+ await adminClient.requestTrialLicense({
43
+ receive_emails_accepted: true,
44
+ terms_accepted: true,
45
+ users: 100,
46
+ contact_name: admin.first_name + ' ' + admin.last_name,
47
+ contact_email: admin.email,
48
+ company_name: 'Mattermost Playwright E2E Tests',
49
+ company_size: '101-250',
50
+ company_country: 'United States',
51
+ });
52
+ }
53
+ catch (error) {
54
+ test.expect(error, 'Failed to request trial license').toBeFalsy();
55
+ throw error;
56
+ }
57
+ }
58
+ async function skipIfNoLicense() {
59
+ const { adminClient } = await init.getAdminClient();
60
+ const license = await adminClient.getClientLicenseOld();
61
+ test.test.skip(license.IsLicensed === 'false', 'Skipping test - server not licensed');
62
+ }
63
+ async function skipIfFeatureFlagNotSet(name, value) {
64
+ const { adminClient } = await init.getAdminClient();
65
+ const cfg = await adminClient.getConfig();
66
+ test.test.skip(cfg.FeatureFlags[name] !== value, `Skipping test - Feature Flag ${name} needs to be set to ${value}`);
67
+ }
68
+
69
+ exports.ensureLicense = ensureLicense;
70
+ exports.requestTrialLicense = requestTrialLicense;
71
+ exports.shouldHaveCallsEnabled = shouldHaveCallsEnabled;
72
+ exports.shouldHaveFeatureFlag = shouldHaveFeatureFlag;
73
+ exports.shouldRunInLinux = shouldRunInLinux;
74
+ exports.skipIfFeatureFlagNotSet = skipIfFeatureFlagNotSet;
75
+ exports.skipIfNoLicense = skipIfNoLicense;
76
+ //# sourceMappingURL=flag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flag.js","sources":["../src/flag.ts"],"sourcesContent":["// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n// See LICENSE.txt for license information.\n\nimport os from 'node:os';\n\nimport {expect, test} from '@playwright/test';\n\nimport {callsPluginId} from './constant';\nimport {getAdminClient} from './server/init';\n\nexport async function shouldHaveCallsEnabled(enabled = true) {\n const {adminClient} = await getAdminClient();\n const config = await adminClient.getConfig();\n\n const callsEnabled = config.PluginSettings.PluginStates[callsPluginId].Enable;\n\n const matched = callsEnabled === enabled;\n expect(matched, matched ? '' : `Calls expect \"${enabled}\" but actual \"${callsEnabled}\"`).toBeTruthy();\n}\n\nexport async function shouldHaveFeatureFlag(name: string, value: string | boolean) {\n const {adminClient} = await getAdminClient();\n const config = await adminClient.getConfig();\n\n const matched = config.FeatureFlags[name] === value;\n expect(\n matched,\n matched ? '' : `FeatureFlags[\"${name}'] expect \"${value}\" but actual \"${config.FeatureFlags[name]}\"`,\n ).toBeTruthy();\n}\n\nexport async function shouldRunInLinux() {\n const platform = os.platform();\n expect(platform, 'Run in Linux or Playwright docker image only').toBe('linux');\n}\n\nexport async function ensureLicense() {\n const {adminClient} = await getAdminClient();\n let license = await adminClient.getClientLicenseOld();\n\n if (license?.IsLicensed !== 'true') {\n const config = await adminClient.getClientConfigOld();\n expect(\n config.ServiceEnvironment === 'dev',\n 'The trial license request fails in the local development environment. Please manually upload the test license.',\n ).toBeFalsy();\n\n await requestTrialLicense();\n\n license = await adminClient.getClientLicenseOld();\n }\n\n expect(license?.IsLicensed === 'true', 'Ensure server has license').toBeTruthy();\n}\n\nexport async function requestTrialLicense() {\n const {adminClient} = await getAdminClient();\n const admin = await adminClient.getMe();\n try {\n await adminClient.requestTrialLicense({\n receive_emails_accepted: true,\n terms_accepted: true,\n users: 100,\n contact_name: admin.first_name + ' ' + admin.last_name,\n contact_email: admin.email,\n company_name: 'Mattermost Playwright E2E Tests',\n company_size: '101-250',\n company_country: 'United States',\n });\n } catch (error) {\n expect(error, 'Failed to request trial license').toBeFalsy();\n throw error;\n }\n}\n\nexport async function skipIfNoLicense() {\n const {adminClient} = await getAdminClient();\n const license = await adminClient.getClientLicenseOld();\n\n test.skip(license.IsLicensed === 'false', 'Skipping test - server not licensed');\n}\n\nexport async function skipIfFeatureFlagNotSet(name: string, value: string | boolean) {\n const {adminClient} = await getAdminClient();\n const cfg = await adminClient.getConfig();\n\n test.skip(cfg.FeatureFlags[name] !== value, `Skipping test - Feature Flag ${name} needs to be set to ${value}`);\n}\n"],"names":["getAdminClient","callsPluginId","expect","test"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Client4 } from '@mattermost/client';
2
+ export declare function baseGlobalSetup(): Promise<void>;
3
+ export declare function ensurePluginsLoaded(client: Client4): Promise<void>;