Skip to content

Conversation

e-maad
Copy link
Contributor

@e-maad e-maad commented Sep 30, 2025

TaskWPB-20570 [Web] Remove Knockout - permission repository

Description

This pull request refactors how permissions are handled across the app, particularly removing direct usage of the PermissionRepository in favor of new permission handler utilities and normalized permission state enums. The changes focus on simplifying permission management for notifications and media streams, and updating related tests and imports to use the new approach.

Permission Handling Refactor:

  • Replaced direct instantiation and usage of PermissionRepository in app.ts, MediaStreamHandler, and NotificationRepository with new permission handler functions (initializePermissions, getPermissionStates, getPermissionState, setPermissionState). This centralizes permission logic and removes the need to pass around repository instances.
  • Updated permission state checks in MediaStreamHandler and NotificationRepository to use the new BrowserPermissionStatus enum and handler functions, replacing previous usage of PermissionStatusState and repository methods.

Notification Permission State Normalization:

  • Introduced normalization of notification permission state in NotificationRepository using normalizePermissionState, ensuring consistent state management and updating via setPermissionState.

Enum and File Renaming:

  • Renamed PermissionStatusState to BrowserPermissionStatus and PermissionState to AppPermissionState for clarity, updating all references and imports accordingly.

Test Updates:

  • Refactored related test files to remove usage of PermissionRepository and update permission state checks to the new enums and handler functions. This includes changes in both media and notification repository tests.

Code Cleanup:

  • Removed unused permission-related members, constructor arguments, and observable properties from repositories, simplifying class interfaces and dependency injection.

This refactor streamlines permission management, improves code clarity, and reduces coupling between repositories and permission logic.

Checklist

  • mentions the JIRA issue in the PR name (Ex. [WPB-XXXX])
  • PR has been self reviewed by the author;
  • Hard-to-understand areas of the code have been commented;
  • If it is a core feature, unit tests have been added;

Copy link

codecov bot commented Sep 30, 2025

Codecov Report

❌ Patch coverage is 73.17073% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.67%. Comparing base (4a50e5e) to head (d5aa418).

Additional details and impacted files
@@            Coverage Diff             @@
##              dev   #19609      +/-   ##
==========================================
+ Coverage   43.61%   43.67%   +0.05%     
==========================================
  Files        1291     1292       +1     
  Lines       32266    32312      +46     
  Branches     7168     7173       +5     
==========================================
+ Hits        14072    14111      +39     
- Misses      16503    16509       +6     
- Partials     1691     1692       +1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

github-actions bot commented Sep 30, 2025

🔗 Download Full Report Artifact

🧪 Playwright Test Summary

  • Passed: 5
  • Failed: 6
  • Skipped: 0
  • 🔁 Flaky: 3
  • 📊 Total: 14
  • Total Runtime: 1080.7s (~ 18 min 1 sec)

Failed Tests:

❌ Calls in channels with device switch and screenshare (tags: TC-8754, crit-flow-web)

Location: specs/CriticalFlow/channelsCall-TC-8755.spec.ts:38
Duration: 36692ms

Errors:

TimeoutError: locator.click: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('[data-uie-name="go-next"]')


   at pageManager/webapp/pages/groupCreation.page.ts:49

  47 |
  48 |   async clickNextButton() {
> 49 |     await this.nextButton.click();
     |                           ^
  50 |   }
  51 |
  52 |   async clickCreateGroupButton() {
    at GroupCreationPage.clickNextButton (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/groupCreation.page.ts:49:27)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/channelsCall-TC-8755.spec.ts:88:40
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/channelsCall-TC-8755.spec.ts:85:5
❌ Channels Management (tags: TC-8752, crit-flow-web)

Location: specs/CriticalFlow/channelsManagement-TC-8752.spec.ts:36
Duration: 43042ms

Errors:

TimeoutError: locator.click: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('[data-uie-name="go-next"]')


   at pageManager/webapp/pages/groupCreation.page.ts:49

  47 |
  48 |   async clickNextButton() {
> 49 |     await this.nextButton.click();
     |                           ^
  50 |   }
  51 |
  52 |   async clickCreateGroupButton() {
    at GroupCreationPage.clickNextButton (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/groupCreation.page.ts:49:27)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/channelsManagement-TC-8752.spec.ts:72:33
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/channelsManagement-TC-8752.spec.ts:69:3
❌ Planning group call with sending various messages during call (tags: TC-8632, crit-flow-web)

Location: specs/CriticalFlow/groupCalls-TC-8632.spec.ts:37
Duration: 25258ms

Errors:

Error: expect(received).toBeFalsy()

Received: true

  125 |     await test.step('Member unmutes themselves', async () => {
  126 |       await memberCalling.unmuteSelfInFullScreen();
> 127 |       expect(await memberCalling.isSelfUserMutedInFullScreen()).toBeFalsy();
      |                                                                 ^
  128 |     });
  129 |
  130 |     await test.step('Validation: Owner sees member is unmuted', async () => {
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupCalls-TC-8632.spec.ts:127:65
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupCalls-TC-8632.spec.ts:125:5
❌ Group Video call (tags: TC-8637, crit-flow-web)

Location: specs/CriticalFlow/groupVideoCall-TC-8637.spec.ts:39
Duration: 53049ms

Errors:

TimeoutError: locator.click: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('[data-uie-name="item-call"]').locator('[data-uie-name="do-call-controls-call-accept"]')


   at pageManager/webapp/pages/calling.page.ts:66

  64 |
  65 |   async clickAcceptCallButton() {
> 66 |     await this.acceptCallButton.click();
     |                                 ^
  67 |   }
  68 |
  69 |   async clickToggleVideoButton() {
    at CallingPage.clickAcceptCallButton (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/calling.page.ts:66:33)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupVideoCall-TC-8637.spec.ts:147:34
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupVideoCall-TC-8637.spec.ts:145:16
❌ Messages in Channels (tags: TC-8753, crit-flow-web)

Location: specs/CriticalFlow/messagesInChannels-TC-8753.spec.ts:44
Duration: 35891ms

Errors:

TimeoutError: locator.click: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('[data-uie-name="go-next"]')


   at pageManager/webapp/pages/groupCreation.page.ts:49

  47 |
  48 |   async clickNextButton() {
> 49 |     await this.nextButton.click();
     |                           ^
  50 |   }
  51 |
  52 |   async clickCreateGroupButton() {
    at GroupCreationPage.clickNextButton (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/groupCreation.page.ts:49:27)
    at setupOwner (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesInChannels-TC-8753.spec.ts:78:42)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesInChannels-TC-8753.spec.ts:89:7
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesInChannels-TC-8753.spec.ts:71:5
❌ Messages in Groups (tags: TC-8751, crit-flow-web)

Location: specs/CriticalFlow/messagesInGroups-TC-8751.spec.ts:42
Duration: 44746ms

Errors:

TimeoutError: locator.waitFor: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('[data-uie-name="item-message"] [data-uie-name="message-reactions"] button[data-uie-name="emoji-pill"][aria-label="1 reaction, react with +1 emoji"]').first() to be visible


   at pageManager/webapp/pages/conversation.page.ts:210

  208 |
  209 |     // Wait for at least one matching element to appear (optional timeout can be set)
> 210 |     await plusOneReactionIcon.first().waitFor({state: 'visible'});
      |                                       ^
  211 |
  212 |     return await plusOneReactionIcon.isVisible();
  213 |   }
    at ConversationPage.isPlusOneReactionVisible (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/conversation.page.ts:210:39)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesInGroups-TC-8751.spec.ts:133:46
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesInGroups-TC-8751.spec.ts:129:5

Flaky Tests:

⚠️ Setting up new device with a backup (tags: TC-8634, crit-flow-web)

Location: specs/CriticalFlow/backupRestoration-TC-8634.spec.ts:35

Attempt 1
Result: ❌ Failed
Duration: 39683ms

Errors:

Error: locator.textContent: Test ended.
Call log:
  - waiting for locator('[data-uie-name="item-message"] .message-body:not(:has(p.text-foreground)):has(.text)').first().locator('.text')


   at pageManager/webapp/pages/conversation.page.ts:167

  165 |
  166 |     for (const message of messages) {
> 167 |       const messageTextContent = await message.locator(selectByClass('text')).textContent();
      |                                                                               ^
  168 |       if (messageTextContent?.trim() === messageText) {
  169 |         return true;
  170 |       }
    at ConversationPage.isMessageVisible (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/conversation.page.ts:167:79)

Attempt 2
Result: ✅ Passed
Duration: 42118ms

⚠️ New person joins team and setups up device (tags: TC-8635, crit-flow-web)

Location: specs/CriticalFlow/joinTeam-TC-8635.spec.ts:37

Attempt 1
Result: ❌ Failed
Duration: 47664ms

Errors:

Error: expect.toBeVisible: Error: strict mode violation: getByText('@Drew Schuppe') resolved to 2 elements:
    1) <span data-uie-name="secondary-line" class="conversation-list-cell-description conversation-list-cell-description--active">Nolan Hoeger: @Drew Schuppe</span> aka getByRole('button', { name: 'Open profile of Crits' })
    2) <span role="button" data-uie-name="label-self-mention" class="message-mention self-mention">…</span> aka getByTestId('label-self-mention')

Call log:
  - Expect "toBeVisible" with timeout 10000ms
  - waiting for getByText('@Drew Schuppe')


  151 |
  152 |     await test.step('A sees the mention in the chat', async () => {
> 153 |       await expect(pages.conversation().page.getByText(`@${memberA.fullName}`)).toBeVisible();
      |                                                                                 ^
  154 |     });
  155 |   },
  156 | );
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/joinTeam-TC-8635.spec.ts:153:81
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/joinTeam-TC-8635.spec.ts:152:16

Attempt 2
Result: ✅ Passed
Duration: 50118ms

⚠️ Personal Account Lifecycle (tags: TC-8638, crit-flow-web)

Location: specs/CriticalFlow/personalAccountLifecycle-TC-8638.spec.ts:31

Attempt 1
Result: ❌ Failed
Duration: 905ms

Errors:

AxiosError: Request failed with status code 409

   at backend/userRepository.e2e.ts:35

  33 |
  34 |   public async setUniqueUsername(username: string, token: string) {
> 35 |     await this.axiosInstance.put(
     |     ^
  36 |       'self/handle',
  37 |       {handle: username},
  38 |       {
    at settle (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/node_modules/axios/lib/core/settle.js:19:12)
    at Unzip.handleStreamEnd (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/node_modules/axios/lib/adapters/http.js:599:11)
    at Axios.request (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/node_modules/axios/lib/core/Axios.js:45:41)
    at UserRepositoryE2E.setUniqueUsername (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/backend/userRepository.e2e.ts:35:5)
    at ApiManagerE2E.createPersonalUser (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/backend/apiManager.e2e.ts:97:5)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/personalAccountLifecycle-TC-8638.spec.ts:48:9
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/personalAccountLifecycle-TC-8638.spec.ts:46:5
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/personalAccountLifecycle-TC-8638.spec.ts:45:3

Attempt 2
Result: ✅ Passed
Duration: 56383ms

state: PermissionState | PermissionStatusState | NotificationPermission,
): UnifiedPermissionState {
switch (state) {
case 'default':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why we need both default and prompt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both default (Notification API) and prompt (Permissions API) represent the same "not yet requested" state but come from different browser APIs, so they're normalized to a unified PROMPT state.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the naming is not perfect, since we have case "default" and default in the same switch statement

@e-maad e-maad requested a review from arjita-mitra October 1, 2025 19:47
Copy link

sonarqubecloud bot commented Oct 2, 2025

@@ -0,0 +1,141 @@
/*
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Permission repository replaced with handlers and pure functions.

* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
Copy link
Member

@aweiss-dev aweiss-dev Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we separate the hookless store from the hook, please?

like:
Permissions.store.ts
Permissions.hooks.ts
Permissions.types.ts (for exported common types, not for types that belong to the store for example)

* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

Copy link
Member

@aweiss-dev aweiss-dev Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we refactor already, can we move that file into the folder it belongs and call it Permissions.test.ts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants