diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8776690e..47a628b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@ and this project adheres to
- ♿(frontend) improve accessibility for decorative images in editor #1282
- #1338
- #1281
+ - #1271
- ♻️(backend) fallback to email identifier when no name #1298
- 🐛(backend) allow ASCII characters in user sub field #1295
- ⚡️(frontend) improve fallback width calculation #1333
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts
index 43a855cf..f2b43299 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts
@@ -45,8 +45,8 @@ test.describe('Doc Create', () => {
})
.click();
- const input = page.getByRole('textbox', { name: 'doc title input' });
- await expect(input).toHaveText('');
+ const input = page.getByRole('textbox', { name: 'Document title' });
+ await expect(input).toHaveText('', { timeout: 10000 });
await expect(
page.locator('.c__tree-view--row-content').getByText('Untitled document'),
).toBeVisible();
@@ -67,8 +67,8 @@ test.describe('Doc Create', () => {
.getByText('New sub-doc')
.click();
- const input = page.getByRole('textbox', { name: 'doc title input' });
- await expect(input).toHaveText('');
+ const input = page.getByRole('textbox', { name: 'Document title' });
+ await expect(input).toHaveText('', { timeout: 10000 });
await expect(
page.locator('.c__tree-view--row-content').getByText('Untitled document'),
).toBeVisible();
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts
index 5d8feab0..6450da39 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts
@@ -40,7 +40,7 @@ test.describe('Doc Export', () => {
await expect(
page.getByRole('button', { name: 'Close the modal' }),
).toBeVisible();
- await expect(page.getByTestId('modal-download-button')).toBeVisible();
+ await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
});
test('it exports the doc with pdf line break', async ({
@@ -81,12 +81,7 @@ test.describe('Doc Export', () => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
- void page
- .getByRole('button', {
- name: 'Download',
- exact: true,
- })
- .click();
+ void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -131,13 +126,13 @@ test.describe('Doc Export', () => {
await page.getByRole('combobox', { name: 'Format' }).click();
await page.getByRole('option', { name: 'Docx' }).click();
- await expect(page.getByTestId('modal-download-button')).toBeVisible();
+ await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.docx`);
});
- void page.getByTestId('modal-download-button').click();
+ void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.docx`);
@@ -203,7 +198,7 @@ test.describe('Doc Export', () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
- await expect(page.getByTestId('modal-download-button')).toBeVisible();
+ await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
const responseCorsPromise = page.waitForResponse(
(response) =>
@@ -214,7 +209,7 @@ test.describe('Doc Export', () => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
- void page.getByTestId('modal-download-button').click();
+ void page.getByTestId('doc-export-download-button').click();
const responseCors = await responseCorsPromise;
expect(responseCors.ok()).toBe(true);
@@ -256,13 +251,13 @@ test.describe('Doc Export', () => {
})
.click();
- await expect(page.getByTestId('modal-download-button')).toBeVisible();
+ await expect(page.getByTestId('doc-export-download-button')).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
- void page.getByTestId('modal-download-button').click();
+ void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -298,13 +293,15 @@ test.describe('Doc Export', () => {
})
.click();
- await expect(page.getByTestId('modal-download-button')).toBeVisible();
+ await expect(
+ page.getByTestId('doc-open-modal-download-button'),
+ ).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
- void page.getByTestId('modal-download-button').click();
+ void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -350,13 +347,15 @@ test.describe('Doc Export', () => {
})
.click();
- await expect(page.getByTestId('modal-download-button')).toBeVisible();
+ await expect(
+ page.getByTestId('doc-open-modal-download-button'),
+ ).toBeVisible();
const downloadPromise = page.waitForEvent('download', (download) => {
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
});
- void page.getByTestId('modal-download-button').click();
+ void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -392,14 +391,9 @@ test.describe('Doc Export', () => {
})
.click();
- await page.waitForURL('**/docs/**', {
- timeout: 10000,
- waitUntil: 'domcontentloaded',
- });
-
- const input = page.getByLabel('doc title input');
+ const input = page.locator('.--docs--doc-title-input[role="textbox"]');
await expect(input).toBeVisible();
- await expect(input).toHaveText('');
+ await expect(input).toHaveText('', { timeout: 10000 });
await input.click();
await input.fill(randomDocFrench);
await input.blur();
@@ -418,7 +412,7 @@ test.describe('Doc Export', () => {
return download.suggestedFilename().includes(`${randomDocFrench}.pdf`);
});
- void page.getByTestId('modal-download-button').click();
+ void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${randomDocFrench}.pdf`);
@@ -453,19 +447,23 @@ test.describe('Doc Export', () => {
await page.locator('.bn-block-outer').last().fill('/');
await page.getByText('Link a doc').first().click();
- await page
- .locator(
- "span[data-inline-content-type='interlinkingSearchInline'] input",
- )
- .fill('interlink-child');
+ const input = page.locator(
+ "span[data-inline-content-type='interlinkingSearchInline'] input",
+ );
+ const searchContainer = page.locator('.quick-search-container');
- await page
- .locator('.quick-search-container')
- .getByText('interlink-child')
- .click();
+ await input.fill('export-interlink');
- const interlink = page.getByRole('link', {
- name: 'interlink-child',
+ await expect(searchContainer).toBeVisible();
+ await expect(searchContainer.getByText(randomDoc)).toBeVisible();
+
+ // We are in docChild, we want to create a link to randomDoc (parent)
+ await searchContainer.getByText(randomDoc).click();
+
+ // Search the interlinking link in the editor (not in the document tree)
+ const editor = page.locator('.ProseMirror.bn-editor');
+ const interlink = editor.getByRole('link', {
+ name: randomDoc,
});
await expect(interlink).toBeVisible();
@@ -480,7 +478,7 @@ test.describe('Doc Export', () => {
})
.click();
- void page.getByTestId('modal-download-button').click();
+ void page.getByTestId('doc-export-download-button').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${docChild}.pdf`);
@@ -488,6 +486,6 @@ test.describe('Doc Export', () => {
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
const pdfData = await pdf(pdfBuffer);
- expect(pdfData.text).toContain('interlink-child'); // This is the pdf text
+ expect(pdfData.text).toContain(randomDoc);
});
});
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
index b76f718e..c707163e 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
@@ -25,7 +25,7 @@ test.describe('Doc Header', () => {
'It is the card information about the document.',
);
- const docTitle = card.getByRole('textbox', { name: 'doc title input' });
+ const docTitle = card.getByRole('textbox', { name: 'Document title' });
await expect(docTitle).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
@@ -54,7 +54,7 @@ test.describe('Doc Header', () => {
test('it updates the title doc', async ({ page, browserName }) => {
await createDoc(page, 'doc-update', browserName, 1);
- const docTitle = page.getByRole('textbox', { name: 'doc title input' });
+ const docTitle = page.getByRole('textbox', { name: 'Document title' });
await expect(docTitle).toBeVisible();
await docTitle.fill('Hello World');
await docTitle.blur();
@@ -66,7 +66,7 @@ test.describe('Doc Header', () => {
browserName,
}) => {
await createDoc(page, 'doc-update', browserName, 1);
- const docTitle = page.getByRole('textbox', { name: 'doc title input' });
+ const docTitle = page.getByRole('textbox', { name: 'Document title' });
await expect(docTitle).toBeVisible();
await docTitle.fill('👍 Hello Emoji World');
await docTitle.blur();
@@ -228,23 +228,27 @@ test.describe('Doc Header', () => {
await page.getByRole('button', { name: 'Share' }).click();
- const shareModal = page.getByLabel('Share modal');
+ const shareModal = page.getByRole('dialog', {
+ name: 'Share modal content',
+ });
+ await expect(shareModal).toBeVisible();
await expect(page.getByText('Share the document')).toBeVisible();
await expect(page.getByPlaceholder('Type a name or email')).toBeHidden();
const invitationCard = shareModal.getByLabel('List invitation card');
+ await expect(invitationCard).toBeVisible();
await expect(
invitationCard.getByText('test@invitation.test').first(),
).toBeVisible();
- await expect(invitationCard.getByLabel('doc-role-text')).toBeVisible();
+ await expect(invitationCard.getByLabel('Document role text')).toBeVisible();
await expect(
invitationCard.getByRole('button', { name: 'more_horiz' }),
).toBeHidden();
const memberCard = shareModal.getByLabel('List members card');
await expect(memberCard.getByText('test@accesses.test')).toBeVisible();
- await expect(memberCard.getByLabel('doc-role-text')).toBeVisible();
+ await expect(memberCard.getByLabel('Document role text')).toBeVisible();
await expect(
memberCard.getByRole('button', { name: 'more_horiz' }),
).toBeHidden();
@@ -296,17 +300,18 @@ test.describe('Doc Header', () => {
await expect(page.getByPlaceholder('Type a name or email')).toBeHidden();
const invitationCard = shareModal.getByLabel('List invitation card');
+ await expect(invitationCard).toBeVisible();
await expect(
invitationCard.getByText('test@invitation.test').first(),
).toBeVisible();
- await expect(invitationCard.getByLabel('doc-role-text')).toBeVisible();
+ await expect(invitationCard.getByLabel('Document role text')).toBeVisible();
await expect(
invitationCard.getByRole('button', { name: 'more_horiz' }),
).toBeHidden();
const memberCard = shareModal.getByLabel('List members card');
await expect(memberCard.getByText('test@accesses.test')).toBeVisible();
- await expect(memberCard.getByLabel('doc-role-text')).toBeVisible();
+ await expect(memberCard.getByLabel('Document role text')).toBeVisible();
await expect(
memberCard.getByRole('button', { name: 'more_horiz' }),
).toBeHidden();
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-routing.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-routing.spec.ts
index 1d64435f..2c084eec 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-routing.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-routing.spec.ts
@@ -60,32 +60,37 @@ test.describe('Doc Routing', () => {
await page.locator('.ProseMirror.bn-editor').fill('Hello World');
- const responsePromise = page.route(
- /.*\/documents\/.*\/$|users\/me\/$/,
- async (route) => {
- const request = route.request();
+ // Wait for the doc link (via its dynamic title) to be visible
+ const docLink = page.getByRole('link', { name: docTitle });
+ await expect(docLink).toBeVisible();
- if (
- request.method().includes('PATCH') ||
- request.method().includes('GET')
- ) {
- await route.fulfill({
- status: 401,
- json: {
- detail: 'Log in to access the document',
- },
- });
- } else {
- await route.continue();
- }
- },
+ // Intercept GET/PATCH requests to return 401
+ await page.route(/.*\/documents\/.*\/$|users\/me\/$/, async (route) => {
+ const request = route.request();
+ if (
+ request.method().includes('PATCH') ||
+ request.method().includes('GET')
+ ) {
+ await route.fulfill({
+ status: 401,
+ json: { detail: 'Log in to access the document' },
+ });
+ } else {
+ await route.continue();
+ }
+ });
+
+ // Explicitly wait for a 401 response after clicking
+ const wait401 = page.waitForResponse(
+ (resp) =>
+ resp.status() === 401 &&
+ /\/(documents\/[^/]+\/|users\/me\/)$/.test(resp.url()),
);
- await page.getByRole('link', { name: '401-doc-parent' }).click();
+ await docLink.click();
+ await wait401;
- await responsePromise;
-
- await expect(page.getByText('Log in to access the document')).toBeVisible({
+ await expect(page.getByText('Log in to access the document.')).toBeVisible({
timeout: 10000,
});
});
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-tree.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-tree.spec.ts
index a22c38ba..a0a155ff 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-tree.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-tree.spec.ts
@@ -50,7 +50,7 @@ test.describe('Doc Tree', () => {
await expect(subPageItem).toBeVisible();
await subPageItem.click();
await verifyDocName(page, '');
- const input = page.getByRole('textbox', { name: 'doc title input' });
+ const input = page.getByRole('textbox', { name: 'Document title' });
await input.click();
const [randomDocName] = randomName('doc-tree-test', browserName, 1);
await input.fill(randomDocName);
@@ -196,7 +196,7 @@ test.describe('Doc Tree', () => {
await page.getByText('Move to my docs').click();
await expect(
- page.getByRole('textbox', { name: 'doc title input' }),
+ page.getByRole('textbox', { name: 'Document title' }),
).not.toHaveText(docChild);
const header = page.locator('header').first();
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts
index dbab861d..504f0b4e 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts
@@ -101,10 +101,9 @@ export const createDoc = async (
waitUntil: 'networkidle',
});
- const input = page.getByLabel('doc title input');
+ const input = page.getByLabel('Document title');
await expect(input).toBeVisible();
await expect(input).toHaveText('');
- await input.click();
await input.fill(randomDocs[i]);
await input.blur();
@@ -120,10 +119,11 @@ export const verifyDocName = async (page: Page, docName: string) => {
timeout: 10000,
});
+ /*replace toHaveText with toContainText to handle cases where emojis or other characters might be added*/
try {
await expect(
- page.getByRole('textbox', { name: 'doc title input' }),
- ).toHaveText(docName);
+ page.getByRole('textbox', { name: 'Document title' }),
+ ).toContainText(docName);
} catch {
await expect(page.getByRole('heading', { name: docName })).toBeVisible();
}
@@ -182,9 +182,9 @@ export const goToGridDoc = async (
};
export const updateDocTitle = async (page: Page, title: string) => {
- const input = page.getByLabel('doc title input');
- await expect(input).toBeVisible();
+ const input = page.getByRole('textbox', { name: 'Document title' });
await expect(input).toHaveText('');
+ await expect(input).toBeVisible();
await input.click();
await input.fill(title);
await input.click();
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
index 39df90be..b23a94eb 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
@@ -145,12 +145,12 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
{t('Cancel')}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx
index f8b404f6..c1e9fb24 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx
@@ -105,7 +105,7 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
}, [doc]);
return (
-
+
{
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
- aria-label="doc title input"
+ aria-label={`${t('Document title')}`}
+ aria-multiline={false}
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
index f0c11928..778a99b2 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
@@ -215,7 +215,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
>
}
@@ -233,9 +233,15 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
{!isSmallMobile && ModalExport && (
+
}
onClick={() => {
setIsModalExportOpen(true);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx
index 672947ac..ab11f0a2 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx
@@ -83,7 +83,7 @@ export const SimpleDocItem = ({
+
{transRole(currentRole)}
);
diff --git a/src/frontend/apps/impress/src/i18n/translations.json b/src/frontend/apps/impress/src/i18n/translations.json
index 217fddbf..cddc96cd 100644
--- a/src/frontend/apps/impress/src/i18n/translations.json
+++ b/src/frontend/apps/impress/src/i18n/translations.json
@@ -302,8 +302,11 @@
"Document accessible to any connected person": "Dokument für jeden angemeldeten Benutzer zugänglich",
"Document duplicated successfully!": "Dokument erfolgreich dupliziert!",
"Document owner": "Besitzer des Dokuments",
+ "Document title": "Dokumenttitel",
+ "Document sections": "Dokumentabschnitte",
"Docx": "Docx",
"Download": "Herunterladen",
+ "Download the document": "Dokument herunterladen",
"Download anyway": "Trotzdem herunterladen",
"Download your document in a .docx or .pdf format.": "Ihr Dokument als .docx- oder .pdf-Datei herunterladen.",
"Duplicate": "Duplizieren",
@@ -451,6 +454,7 @@
"en": {
"translation": {
"Back to homepage": "Back to Docs homepage",
+ "Document title": "Document title",
"Search docs": "Search docs",
"More options": "More options",
"Pinned documents": "Pinned documents",
@@ -527,8 +531,11 @@
"Docs: Your new companion to collaborate on documents efficiently, intuitively, and securely.": "Docs: su nuevo compañero para colaborar en documentos de forma eficiente, intuitiva y segura.",
"Document accessible to any connected person": "Documento accesible a cualquier persona conectada",
"Document owner": "Propietario del documento",
+ "Document title": "Título del documento",
+ "Document sections": "Secciones del documento",
"Docx": "Docx",
"Download": "Descargar",
+ "Download the document": "Descargar el documento",
"Download anyway": "Descargar de todos modos",
"Download your document in a .docx or .pdf format.": "Descargue su documento en formato .docx o .pdf.",
"Editor": "Editor",
@@ -726,10 +733,12 @@
"Document duplicated successfully!": "Document dupliqué avec succès !",
"Document emoji icon": "Émoticônes du document",
"Document owner": "Propriétaire du document",
+ "Document title": "Titre du document",
"Document sections": "Sections du document",
"Document visibility": "Visibilité du document",
"Docx": "Docx",
"Download": "Télécharger",
+ "Download the document": "Télécharger le document",
"Download anyway": "Télécharger malgré tout",
"Download your document in a .docx or .pdf format.": "Téléchargez votre document au format .docx ou .pdf.",
"Duplicate": "Dupliquer",
@@ -1105,8 +1114,11 @@
"Docs: Your new companion to collaborate on documents efficiently, intuitively, and securely.": "Docs: Je nieuwe metgezel om efficiënt, intuïtief en veilig samen te werken aan documenten.",
"Document accessible to any connected person": "Document is toegankelijk voor ieder verbonden persoon",
"Document owner": "Document eigenaar",
+ "Document title": "Documenttitel",
+ "Document sections": "Document secties",
"Docx": "Docx",
"Download": "Download",
+ "Download the document": "Document downloaden",
"Download anyway": "Download alsnog",
"Download your document in a .docx or .pdf format.": "Download jouw document in .docx of .pdf formaat.",
"Editor": "Bewerker",