Skip to content

Conversation

@e-maad
Copy link
Contributor

@e-maad e-maad commented Nov 28, 2025

TaskWPB-21650 [Web] Collabora integration

Pull Request

Summary

We have to provide users a feature where they can collaborate on files. Like real time editing as a team.
With Collabora integration, we can allow user to achieve it.

Security Checklist (required)

  • External inputs are validated & sanitized on client and/or server where applicable.
  • API responses are validated; unexpected shapes are handled safely (fallbacks or errors).
  • No unsafe HTML is rendered; if unavoidable, sanitization is applied and documented where it happens.
  • Injection risks (XSS/SQL/command) are prevented via safe APIs and/or escaping.

Standards Acknowledgement (required)

Screenshots or demo (if the user interface changed)

Notes for reviewers

Screen.Recording.2025-11-28.at.16.07.26.mov
  • Trade-offs:
    We are using <iframe> to render the URL as we don't have native integration for Collabora.
  • Follow-ups (linked issues):
  • Linked PRs (e.g. web-packages):

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
5.2% Duplication on New Code (required ≤ 5%)

See analysis details on SonarQube Cloud

Copilot finished reviewing on behalf of e-maad November 28, 2025 15:14
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR integrates Collabora Online for real-time collaborative document editing via iframe embedding. Users can now edit supported file types (docx, xlsx, pptx, odf) directly within the Wire webapp through a toggle between "Viewing" and "Editing" modes in the file preview modal.

Key Changes:

  • Added FileEditor component that renders Collabora URLs in an iframe with auto-refresh logic before URL expiry
  • Extended file preview modals with edit mode state management and UI toggle buttons
  • Implemented isFileEditable() utility to determine which file extensions support collaborative editing
  • Propagated asset IDs (UUIDs) through component hierarchy replacing React useId() for consistent identification

Reviewed changes

Copilot reviewed 30 out of 31 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
src/script/components/FileFullscreenModal/FileEditor/FileEditor.tsx New component rendering Collabora iframe with URL refresh logic
src/script/components/FileFullscreenModal/FileEditor/FileEditor.styles.ts Styles for the iframe editor (full width/height)
src/script/components/FileFullscreenModal/FileFullscreenModal.tsx Added edit mode state and conditional rendering of FileEditor vs standard preview
src/script/components/FileFullscreenModal/FileHeader/FileHeader.tsx Added Viewing/Editing toggle buttons and updated download logic to fetch fresh presigned URLs
src/script/components/FileFullscreenModal/FileHeader/FileHeader.styles.ts Styles for edit mode toggle button group
src/script/util/FileTypeUtil.ts New isFileEditable() function checking if file extension supports editing
src/script/util/FileTypeUtil.test.ts Comprehensive tests for isFileEditable() covering various edge cases
src/script/util/getFileTypeFromExtension/getFileTypeFromExtension.ts Added 'odf' to document extensions list
src/script/repositories/cells/CellsRepository.ts Extended getNode() to accept flags and options parameters
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/FileAssetCard/common/FileAssetOptions/FileAssetOptions.tsx Added "Edit" option to file dropdown menu for editable files
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/FileAssetCard/common/FileAssetOptions/FileAssetOptions.test.tsx Tests verifying edit option behavior with different file types
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/FileAssetCard/common/FilePreviewModal/FilePreviewModal.tsx Pass through isEditMode prop
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/FileAssetCard/FileAssetWithPreview/FileAssetWithPreview.tsx Track edit mode state and handle modal open/close with edit flag
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/FileAssetCard/FileAssetSmall/FileAssetSmall.tsx Track edit mode state and pass through asset ID
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/FileAssetCard/FileAssetCard.tsx Pass through asset ID prop
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/MultipartAssets.tsx Pass asset UUID as ID to child components
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/ImageAssetCard/* Replace useId() with prop-based ID (UUID)
src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/VideoAssetCard/* Replace useId() with prop-based ID (UUID)
src/script/components/Conversation/ConversationCells/CellsTable/common/CellsFilePreviewModalContext/CellsFilePreviewModalContext.tsx Add isEditMode state and parameter to handleOpenFile
src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsTableRowOptions.tsx Add "Edit" option for editable files in table row dropdown
src/script/components/Conversation/ConversationCells/CellsTable/CellsFilePreviewModal/CellsFilePreviewModal.tsx Use selectedFile.id instead of context ID, pass isEditMode
src/script/components/CellsGlobalView/CellsTable/common/CellsFilePreviewModalContext/CellsFilePreviewModalContext.tsx Add isEditMode state and parameter to handleOpenFile
src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsTableRowOptions.tsx Add "Edit" option for editable files in table row dropdown
src/script/components/CellsGlobalView/CellsTable/CellsFilePreviewModal/CellsFilePreviewModal.tsx Use selectedFile.id instead of context ID, pass isEditMode
src/i18n/en-US.json Added translation keys for edit option and editor error messages
src/types/i18n.d.ts Type definitions for new i18n keys

Comment on lines +76 to +78
if (fileUrl) {
const node = await cellsRepository.getNode({uuid: id, flags: []});
await forcedDownloadFile({url: node.PreSignedGET?.Url || fileUrl, name: fileNameWithExtension});
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[Important] The handleFileDownload function lacks error handling. If the API call or download fails, the user will get no feedback.

Add try-catch with user-facing error handling:

const handleFileDownload = async () => {
  if (!fileUrl) return;
  
  try {
    const node = await cellsRepository.getNode({uuid: id, flags: []});
    await forcedDownloadFile({url: node.PreSignedGET?.Url || fileUrl, name: fileNameWithExtension});
  } catch (error) {
    // Show user-facing error notification
    console.error('Failed to download file:', error);
    // Consider adding a toast notification here
  }
};
Suggested change
if (fileUrl) {
const node = await cellsRepository.getNode({uuid: id, flags: []});
await forcedDownloadFile({url: node.PreSignedGET?.Url || fileUrl, name: fileNameWithExtension});
if (!fileUrl) return;
try {
const node = await cellsRepository.getNode({uuid: id, flags: []});
await forcedDownloadFile({url: node.PreSignedGET?.Url || fileUrl, name: fileNameWithExtension});
} catch (error) {
// Show user-facing error notification
console.error('Failed to download file:', error);
// If a toast notification system is available, use it:
// toast.error('Failed to download file. Please try again.');
// Otherwise, fallback to alert:
alert('Failed to download file. Please try again.');

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +97
export const FileEditor = ({id}: FileEditorProps) => {
const cellsRepository = container.resolve(CellsRepository);
const [node, setNode] = useState<Node | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);

const fetchNode = useCallback(async () => {
try {
setIsLoading(true);
setIsError(false);
const fetchedNode = await cellsRepository.getNode({uuid: id, flags: ['WithEditorURLs']});
setNode(fetchedNode);
} catch (err) {
setIsError(true);
} finally {
setIsLoading(false);
}
}, [id, cellsRepository]);

// Initial fetch
useEffect(() => {
void fetchNode();
}, [id, cellsRepository, fetchNode]);

// Auto-refresh mechanism before expiry
useEffect(() => {
if (!node?.EditorURLs?.collabora.ExpiresAt) {
return;
}

const expiresInSeconds = Number(node.EditorURLs.collabora.ExpiresAt);
const refreshInSeconds = expiresInSeconds - REFRESH_BUFFER_SECONDS;

// Set timeout to refresh before expiry
const timeoutId = setTimeout(() => {
void fetchNode();
}, refreshInSeconds * MILLISECONDS_IN_SECOND);

return () => {
clearTimeout(timeoutId);
};
}, [node, fetchNode]);

if (isLoading) {
return <FileLoader />;
}

if (isError || !node) {
return <div>{t('fileFullscreenModal.editor.error')}</div>;
}

return (
<iframe
css={styles.editorIframe}
src={node.EditorURLs?.collabora.Url}
title={t('fileFullscreenModal.editor.iframeTitle')}
/>
);
};
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[Important] The new FileEditor component lacks test coverage. Given its security-sensitive nature (rendering iframes with external URLs), it should have comprehensive tests covering:

  • URL validation scenarios
  • Error states
  • Loading states
  • Timeout refresh logic
  • Edge cases with expiry times

Consider adding tests in a new FileEditor.test.tsx file.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +79
// Set timeout to refresh before expiry
const timeoutId = setTimeout(() => {
void fetchNode();
}, refreshInSeconds * MILLISECONDS_IN_SECOND);

return () => {
clearTimeout(timeoutId);
};
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[Blocker] The timeout calculation can create negative or zero timeouts when expiresInSeconds is less than or equal to REFRESH_BUFFER_SECONDS (10 seconds). This will cause immediate re-fetching in a loop.

Add validation:

const expiresInSeconds = Number(node.EditorURLs.collabora.ExpiresAt);
const refreshInSeconds = Math.max(0, expiresInSeconds - REFRESH_BUFFER_SECONDS);

// Only set timeout if there's meaningful time left
if (refreshInSeconds > 0) {
  const timeoutId = setTimeout(() => {
    void fetchNode();
  }, refreshInSeconds * MILLISECONDS_IN_SECOND);
  
  return () => clearTimeout(timeoutId);
}
Suggested change
// Set timeout to refresh before expiry
const timeoutId = setTimeout(() => {
void fetchNode();
}, refreshInSeconds * MILLISECONDS_IN_SECOND);
return () => {
clearTimeout(timeoutId);
};
// Only set timeout if there's meaningful time left
if (refreshInSeconds > 0) {
const timeoutId = setTimeout(() => {
void fetchNode();
}, refreshInSeconds * MILLISECONDS_IN_SECOND);
return () => {
clearTimeout(timeoutId);
};
}

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +95
<iframe
css={styles.editorIframe}
src={node.EditorURLs?.collabora.Url}
title={t('fileFullscreenModal.editor.iframeTitle')}
/>
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[Important] The iframe lacks security attributes recommended for embedding external content. Add sandbox and other security attributes:

<iframe
  css={styles.editorIframe}
  src={node.EditorURLs?.collabora.Url}
  title={t('fileFullscreenModal.editor.iframeTitle')}
  sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals"
  referrerPolicy="no-referrer"
/>

Note: Adjust the sandbox permissions based on actual Collabora requirements - use the most restrictive set that still allows the editor to function.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +105 to +123
<button
title="Viewing"
aria-label="Viewing"
className={!isInEditMode ? 'active' : ''}
onClick={() => onEditModeChange(false)}
>
<ShowIcon width={16} height={16} />
Viewing
</button>
<button
title="Editing"
aria-label="Editing"
className={isInEditMode ? 'active' : ''}
onClick={() => onEditModeChange(true)}
>
<EditIcon width={14} height={14} />
Editing
</button>
</div>
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[Important] The toggle buttons lack proper accessibility attributes. They should include role="button", type="button", and use aria-pressed instead of relying only on CSS classes for state indication.

<button
  type="button"
  role="button"
  title="Viewing"
  aria-label="Viewing"
  aria-pressed={!isInEditMode}
  className={!isInEditMode ? 'active' : ''}
  onClick={() => onEditModeChange(false)}
>
  <ShowIcon width={16} height={16} />
  Viewing
</button>

Apply the same pattern to the "Editing" button with aria-pressed={isInEditMode}.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +70 to +79
const refreshInSeconds = expiresInSeconds - REFRESH_BUFFER_SECONDS;

// Set timeout to refresh before expiry
const timeoutId = setTimeout(() => {
void fetchNode();
}, refreshInSeconds * MILLISECONDS_IN_SECOND);

return () => {
clearTimeout(timeoutId);
};
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[Bug] Using Number() on ExpiresAt can produce NaN if the value is not a valid number. This would make refreshInSeconds also NaN, leading to unexpected behavior. Add validation:

const expiresInSeconds = Number(node.EditorURLs.collabora.ExpiresAt);

if (isNaN(expiresInSeconds) || expiresInSeconds <= 0) {
  return;
}

const refreshInSeconds = Math.max(0, expiresInSeconds - REFRESH_BUFFER_SECONDS);

if (refreshInSeconds > 0) {
  const timeoutId = setTimeout(() => {
    void fetchNode();
  }, refreshInSeconds * MILLISECONDS_IN_SECOND);

  return () => clearTimeout(timeoutId);
}
Suggested change
const refreshInSeconds = expiresInSeconds - REFRESH_BUFFER_SECONDS;
// Set timeout to refresh before expiry
const timeoutId = setTimeout(() => {
void fetchNode();
}, refreshInSeconds * MILLISECONDS_IN_SECOND);
return () => {
clearTimeout(timeoutId);
};
if (isNaN(expiresInSeconds) || expiresInSeconds <= 0) {
return;
}
const refreshInSeconds = Math.max(0, expiresInSeconds - REFRESH_BUFFER_SECONDS);
// Set timeout to refresh before expiry
if (refreshInSeconds > 0) {
const timeoutId = setTimeout(() => {
void fetchNode();
}, refreshInSeconds * MILLISECONDS_IN_SECOND);
return () => {
clearTimeout(timeoutId);
};
}

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +78
if (fileUrl) {
const node = await cellsRepository.getNode({uuid: id, flags: []});
await forcedDownloadFile({url: node.PreSignedGET?.Url || fileUrl, name: fileNameWithExtension});
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[Important] The download function fetches fresh node data but doesn't validate that PreSignedGET?.Url is present before falling back to fileUrl. If both are undefined or invalid URLs, the download may fail silently. Add validation:

const handleFileDownload = async () => {
  if (!fileUrl) return;
  
  try {
    const node = await cellsRepository.getNode({uuid: id, flags: []});
    const downloadUrl = node.PreSignedGET?.Url || fileUrl;
    
    if (!downloadUrl) {
      console.error('No valid download URL available');
      return;
    }
    
    await forcedDownloadFile({url: downloadUrl, name: fileNameWithExtension});
  } catch (error) {
    console.error('Failed to download file:', error);
  }
};
Suggested change
if (fileUrl) {
const node = await cellsRepository.getNode({uuid: id, flags: []});
await forcedDownloadFile({url: node.PreSignedGET?.Url || fileUrl, name: fileNameWithExtension});
if (!fileUrl) {
// No fileUrl provided, cannot proceed
console.error('No fileUrl provided for download');
return;
}
try {
const node = await cellsRepository.getNode({uuid: id, flags: []});
const downloadUrl = node.PreSignedGET?.Url || fileUrl;
if (!downloadUrl || typeof downloadUrl !== 'string' || downloadUrl.trim() === '') {
console.error('No valid download URL available');
return;
}
await forcedDownloadFile({url: downloadUrl, name: fileNameWithExtension});
} catch (error) {
console.error('Failed to download file:', error);

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +53

const EDITABLE_FILE_EXTENSIONS = ['odf', 'docx', 'xlsx', 'pptx'];

Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] [Suggestion] The isFileEditable function and EDITABLE_FILE_EXTENSIONS constant lack JSDoc documentation explaining what makes a file editable and the business logic behind the supported extensions:

/**
 * File extensions that support collaborative editing via Collabora integration.
 * Includes modern Office formats (docx, xlsx, pptx) and OpenDocument formats (odf).
 */
const EDITABLE_FILE_EXTENSIONS = ['odf', 'docx', 'xlsx', 'pptx'];

/**
 * Checks if a file extension is supported for collaborative editing.
 * 
 * @param fileExtension - The file extension to check (without the leading dot)
 * @returns true if the file can be edited with Collabora, false otherwise
 * 
 * @example
 * isFileEditable('docx') // returns true
 * isFileEditable('pdf')  // returns false
 */
export const isFileEditable = (fileExtension: string): boolean => {
  return EDITABLE_FILE_EXTENSIONS.includes(fileExtension.toLowerCase());
};
Suggested change
const EDITABLE_FILE_EXTENSIONS = ['odf', 'docx', 'xlsx', 'pptx'];
/**
* List of file extensions that are supported for collaborative editing via Collabora integration.
*
* Business logic:
* - Only modern Office formats (docx, xlsx, pptx) and OpenDocument format (odf) are supported.
* - These formats can be edited collaboratively in the application.
* - Other formats (e.g., pdf, txt) are not supported for editing.
*/
const EDITABLE_FILE_EXTENSIONS = ['odf', 'docx', 'xlsx', 'pptx'];
/**
* Checks if a file extension is supported for collaborative editing.
*
* @param fileExtension - The file extension to check (without the leading dot).
* @returns {boolean} True if the file can be edited with Collabora, false otherwise.
*
* @example
* isFileEditable('docx') // returns true
* isFileEditable('pdf') // returns false
*/

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +95
<iframe
css={styles.editorIframe}
src={node.EditorURLs?.collabora.Url}
title={t('fileFullscreenModal.editor.iframeTitle')}
/>
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[Important] The iframe src attribute uses an unsanitized URL from the API response (node.EditorURLs?.collabora.Url). While this URL comes from a trusted backend, it should still be validated to ensure it uses HTTPS protocol and doesn't contain javascript: or data: schemes.

Consider adding URL validation:

const validateEditorUrl = (url: string): boolean => {
  try {
    const parsed = new URL(url);
    return parsed.protocol === 'https:';
  } catch {
    return false;
  }
};

// Then in the render:
if (!node.EditorURLs?.collabora.Url || !validateEditorUrl(node.EditorURLs.collabora.Url)) {
  return <div>{t('fileFullscreenModal.editor.error')}</div>;
}

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +65 to +75
const [isEditableState, setIsEditableState] = useState(isEditMode);
const isEditable = isFileEditable(fileExtension);

const onCloseModal = () => {
setIsEditableState(false);
onClose();
};

useEffect(() => {
setIsEditableState(!!isEditMode);
}, [isEditMode]);
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] [Suggestion] The variable name isEditableState is somewhat redundant. Since it's already a local state variable, consider renaming to isInEditMode to match the prop name used in FileHeader (isInEditMode), improving consistency:

const [isInEditMode, setIsInEditMode] = useState(isEditMode);
const isEditable = isFileEditable(fileExtension);

const onCloseModal = () => {
  setIsInEditMode(false);
  onClose();
};

useEffect(() => {
  setIsInEditMode(!!isEditMode);
}, [isEditMode]);

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Contributor

🔗 Download Full Report Artifact

🧪 Playwright Test Summary

  • Passed: 8
  • Failed: 6
  • Skipped: 0
  • 🔁 Flaky: 0
  • 📊 Total: 14
  • Total Runtime: 1173.4s (~ 19 min 33 sec)

Failed Tests:

❌ Account Management (tags: TC-8639, crit-flow-web)

Location: specs/CriticalFlow/accountManagement-TC-8639.spec.ts:37
Duration: 88757ms

Errors:

Error: Matching URL not found in the email body

   at backend/inbucketClient.e2e.ts:120

  118 |
  119 |     if (this.isValidURL(matchingUrl) === false) {
> 120 |       throw new Error('Matching URL not found in the email body');
      |             ^
  121 |     }
  122 |
  123 |     return matchingUrl;
    at InbucketClientE2E.getMatchingURLFromEmailBody (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/backend/inbucketClient.e2e.ts:120:13)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/accountManagement-TC-8639.spec.ts:133:30
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/accountManagement-TC-8639.spec.ts:124:3
❌ Team owner adds whole team to an all team chat (tags: TC-8631, crit-flow-web)

Location: specs/CriticalFlow/addMembersToChat-TC-8631.spec.ts:43
Duration: 53382ms

Errors:

Error: expect(locator).toBeVisible() failed

Locator: locator('[data-uie-name="item-message"][data-uie-send-status="2"]').filter({ hasText: 'Hello from Sandrine!' }).locator('[data-uie-name="message-reactions"]').locator('[data-uie-name="emoji-pill"][title="heart"]')
Expected: visible
Timeout: 10000ms
Error: element(s) not found

Call log:
  - Expect "toBeVisible" with timeout 10000ms
  - waiting for locator('[data-uie-name="item-message"][data-uie-send-status="2"]').filter({ hasText: 'Hello from Sandrine!' }).locator('[data-uie-name="message-reactions"]').locator('[data-uie-name="emoji-pill"][title="heart"]')


  185 |       await pages.conversationList().openConversation(conversationName);
  186 |       const ownerMessage = pages.conversation().getMessage({content: `Hello from ${owner.firstName}!`});
> 187 |       await expect(pages.conversation().getReactionOnMessage(ownerMessage, 'heart')).toBeVisible();
      |                                                                                      ^
  188 |       await expect(pages.conversation().getReactionOnMessage(ownerMessage, 'joy')).toBeVisible();
  189 |
  190 |       // Member1 verifies they can see thumbs up (+1) and joy (😂) reactions on their message from owner and member2
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/addMembersToChat-TC-8631.spec.ts:187:86
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/addMembersToChat-TC-8631.spec.ts:183:5
❌ 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: 150095ms

Errors:

Test timeout of 150000ms exceeded.
❌ Group Video call (tags: TC-8637, crit-flow-web)

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

Errors:

TimeoutError: locator.waitFor: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('#add-participants [data-uie-name="search-list"] [aria-label="Open profile of Dave Nicolas"]').locator('input[type="checkbox"]')


   at pageManager/webapp/pages/conversationDetails.page.ts:68

  66 |       await userLocator.click();
  67 |       // Wait for the user to be selected (checkbox should be checked)
> 68 |       await userLocator.locator('input[type="checkbox"]').waitFor({state: 'attached'});
     |                                                           ^
  69 |     }
  70 |
  71 |     await this.page.locator(`${selectById('add-participants')} ${selectByDataAttribute('do-create')}`).click();
    at ConversationDetailsPage.addUsersToConversation (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/conversationDetails.page.ts:68:59)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupVideoCall-TC-8637.spec.ts:127:7
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupVideoCall-TC-8637.spec.ts:123:5
❌ New person joins team and setups up device (tags: TC-8635, crit-flow-web)

Location: specs/CriticalFlow/joinTeam-TC-8635.spec.ts:38
Duration: 67013ms

Errors:

TimeoutError: locator.waitFor: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('#add-participants [data-uie-name="search-list"] [aria-label="Open profile of Oceane Haag"]').locator('input[type="checkbox"]')


   at pageManager/webapp/pages/conversationDetails.page.ts:68

  66 |       await userLocator.click();
  67 |       // Wait for the user to be selected (checkbox should be checked)
> 68 |       await userLocator.locator('input[type="checkbox"]').waitFor({state: 'attached'});
     |                                                           ^
  69 |     }
  70 |
  71 |     await this.page.locator(`${selectById('add-participants')} ${selectByDataAttribute('do-create')}`).click();
    at ConversationDetailsPage.addUsersToConversation (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/conversationDetails.page.ts:68:59)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/joinTeam-TC-8635.spec.ts:125:7
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/joinTeam-TC-8635.spec.ts:114:5
❌ Messages in 1:1 (tags: TC-8750, crit-flow-web)

Location: specs/CriticalFlow/messagesIn1On1-TC-8750.spec.ts:47
Duration: 62196ms

Errors:

Error: expect(locator).toBeVisible() failed

Locator: locator('[data-uie-name="item-message"][data-uie-send-status="2"]').filter({ hasText: 'This message will self-destruct in 10 seconds.' })
Expected: visible
Timeout: 10000ms
Error: element(s) not found

Call log:
  - Expect "toBeVisible" with timeout 10000ms
  - waiting for locator('[data-uie-name="item-message"][data-uie-send-status="2"]').filter({ hasText: 'This message will self-destruct in 10 seconds.' })


  177 |     await components.inputBarControls().setEphemeralTimerTo('10 seconds');
  178 |     await pages.conversation().sendMessage(selfDestructMessageText);
> 179 |     await expect(pages.conversation().getMessage({content: selfDestructMessageText})).toBeVisible();
      |                                                                                       ^
  180 |   });
  181 |
  182 |   await test.step('User B sees the message', async () => {
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesIn1On1-TC-8750.spec.ts:179:87
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesIn1On1-TC-8750.spec.ts:176:3

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.

3 participants