319 lines · 12.7 KB · TypeScript
RawDownload
// @watch start// web_src/js/features/comp/ComboMarkdownEditor.js// web_src/css/editor/combomarkdowneditor.css// templates/shared/combomarkdowneditor.tmpl// @watch end import {expect} from '@playwright/test';import {accessibilityCheck} from './shared/accessibility.ts';import {save_visual, test} from './utils_e2e.ts'; test.use({user: 'user2'}); test('Markdown image preview behaviour', async ({page}, workerInfo) => {  test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari;');   // Editing the root README.md file for image preview  const editPath = '/user2/repo1/src/branch/master/README.md';   const response = await page.goto(editPath, {waitUntil: 'domcontentloaded'});  expect(response?.status()).toBe(200);   // Click 'Edit file' tab  await page.locator('[data-tooltip-content="Edit file"]').click();   // This yields the monaco editor  const editor = page.getByRole('presentation').nth(0);  await editor.click();  // Clear all the content  await page.keyboard.press('ControlOrMeta+KeyA');  // Add the image  await page.keyboard.type('![Logo of Forgejo](./assets/logo.svg "Logo of Forgejo")');   // Click 'Preview' tab  await page.locator('a[data-tab="preview"]').click();   // Check for the image preview via the expected attribute  const preview = page.locator('div[data-tab="preview"] p[dir="auto"] a');  await expect(preview).toHaveAttribute('href', 'http://localhost:3003/user2/repo1/media/branch/master/assets/logo.svg');  await save_visual(page);}); test('Markdown indentation', async ({page}) => {  const initText = `* first\n* second\n* third\n* last`;   const response = await page.goto('/user2/repo1/issues/new');  expect(response?.status()).toBe(200);   const textarea = page.locator('textarea[name=content]');  const tab = '    ';  const indent = page.locator('button[data-md-action="indent"]');  const unindent = page.locator('button[data-md-action="unindent"]');  await textarea.fill(initText);  await textarea.click(); // Tab handling is disabled until pointer event or input.   // Indent, then unindent first line  await textarea.focus();  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(0, 0));  await indent.click();  await expect(textarea).toHaveValue(`${tab}* first\n* second\n* third\n* last`);  await unindent.click();  await expect(textarea).toHaveValue(initText);   // Indent second line while somewhere inside of it  await textarea.focus();  await textarea.press('ArrowDown');  await textarea.press('ArrowRight');  await textarea.press('ArrowRight');  await indent.click();  await expect(textarea).toHaveValue(`* first\n${tab}* second\n* third\n* last`);   // Subsequently, select a chunk of 2nd and 3rd line and indent both, preserving the cursor position in relation to text  await textarea.focus();  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('hird')));  await indent.click();  const lines23 = `* first\n${tab}${tab}* second\n${tab}* third\n* last`;  await expect(textarea).toHaveValue(lines23);  await expect(textarea).toHaveJSProperty('selectionStart', lines23.indexOf('cond'));  await expect(textarea).toHaveJSProperty('selectionEnd', lines23.indexOf('hird'));   // Then unindent twice, erasing all indents.  await unindent.click();  await expect(textarea).toHaveValue(`* first\n${tab}* second\n* third\n* last`);  await unindent.click();  await expect(textarea).toHaveValue(initText);   // Indent and unindent with cursor at the end of the line  await textarea.focus();  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('cond')));  await textarea.press('End');  await indent.click();  await expect(textarea).toHaveValue(`* first\n${tab}* second\n* third\n* last`);  await unindent.click();  await expect(textarea).toHaveValue(initText);   // Check that Tab does work after input  await textarea.focus();  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length));  await textarea.press('Shift+Enter'); // Avoid triggering the prefix continuation feature  await textarea.pressSequentially('* least');  await indent.click();  await expect(textarea).toHaveValue(`* first\n* second\n* third\n* last\n${tab}* least`);   // Check that partial indents are cleared  await textarea.focus();  await textarea.fill(initText);  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('* second'), it.value.indexOf('* second')));  await textarea.pressSequentially('  ');  await unindent.click();  await expect(textarea).toHaveValue(initText);}); test('Markdown list continuation', async ({page}) => {  const initText = `* first\n* second`;   const response = await page.goto('/user2/repo1/issues/new');  expect(response?.status()).toBe(200);   const textarea = page.locator('textarea[name=content]');  const tab = '    ';  const indent = page.locator('button[data-md-action="indent"]');  await textarea.fill(initText);   // Test continuation of '    * ' prefix  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('rst'), it.value.indexOf('rst')));  await indent.click();  await textarea.press('End');  await textarea.press('Enter');  await textarea.pressSequentially('muddle');  await expect(textarea).toHaveValue(`${tab}* first\n${tab}* muddle\n* second`);   // Test breaking in the middle of a line  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.lastIndexOf('ddle'), it.value.lastIndexOf('ddle')));  await textarea.pressSequentially('tate');  await textarea.press('Enter');  await textarea.pressSequentially('me');  await expect(textarea).toHaveValue(`${tab}* first\n${tab}* mutate\n${tab}* meddle\n* second`);   // Test not triggering when Shift held  await textarea.fill(initText);  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length));  await textarea.press('Shift+Enter');  await textarea.press('Enter');  await textarea.pressSequentially('...but not least');  await expect(textarea).toHaveValue(`* first\n* second\n\n...but not least`);   // Test continuation of ordered list  await textarea.fill(`1. one`);  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length));  await textarea.press('Enter');  await textarea.pressSequentially(' ');  await textarea.press('Enter');  await textarea.pressSequentially('three');  await textarea.press('Enter');  await textarea.press('Enter');  await expect(textarea).toHaveValue(`1. one\n2.  \n3. three\n\n`);   // Test continuation of alternative ordered list syntax  await textarea.fill(`1) one`);  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length));  await textarea.press('Enter');  await textarea.pressSequentially(' ');  await textarea.press('Enter');  await textarea.pressSequentially('three');  await textarea.press('Enter');  await textarea.press('Enter');  await expect(textarea).toHaveValue(`1) one\n2)  \n3) three\n\n`);   // Test continuation of checklists  await textarea.fill(`- [ ]have a problem\n- [x]create a solution`);  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length));  await textarea.press('Enter');  await textarea.pressSequentially('write a test');  await expect(textarea).toHaveValue(`- [ ]have a problem\n- [x]create a solution\n- [ ]write a test`);   // Test all conceivable syntax (except ordered lists)  const prefixes = [    '- ', // A space between the bullet and the content is required.    ' - ', // I have seen single space in front of -/* being used and even recommended, I think.    '* ',    '+ ',    '  ',    '    ',    '    - ',    '\t',    '\t\t* ',    '> ',    '> > ',    '- [ ] ',    '* [ ] ',    '+ [ ] ',  ];  for (const prefix of prefixes) {    await textarea.fill(`${prefix}one`);    await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length));    await textarea.press('Enter');    await textarea.pressSequentially(' ');    await textarea.press('Enter');    await textarea.pressSequentially('two');    await textarea.press('Enter');    await textarea.press('Enter');    await expect(textarea).toHaveValue(`${prefix}one\n${prefix} \n${prefix}two\n\n`);  }}); test('Markdown insert table', async ({page}) => {  const response = await page.goto('/user2/repo1/issues/new');  expect(response?.status()).toBe(200);   const newTableButton = page.locator('button[data-md-action="new-table"]');  await newTableButton.click();   const newTableModal = page.locator('div[data-markdown-table-modal-id="0"]');  await expect(newTableModal).toBeVisible();  await save_visual(page);   await newTableModal.locator('input[name="table-rows"]').fill('3');  await newTableModal.locator('input[name="table-columns"]').fill('2');   await newTableModal.locator('button[data-selector-name="ok-button"]').click();   await expect(newTableModal).toBeHidden();   const textarea = page.locator('textarea[name=content]');  await expect(textarea).toHaveValue('| Header  | Header  |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n');  await save_visual(page);}); test('Markdown insert link', async ({page}) => {  const response = await page.goto('/user2/repo1/issues/new');  expect(response?.status()).toBe(200);   const newLinkButton = page.locator('button[data-md-action="new-link"]');  await newLinkButton.click();   const newLinkModal = page.locator('div[data-markdown-link-modal-id="0"]');  await expect(newLinkModal).toBeVisible();  await accessibilityCheck({page}, ['[data-modal-name="new-markdown-link"]'], [], []);  await save_visual(page);   const url = 'https://example.com';  const description = 'Where does this lead?';   await newLinkModal.locator('input[name="link-url"]').fill(url);  await newLinkModal.locator('input[name="link-description"]').fill(description);   await newLinkModal.locator('button[data-selector-name="ok-button"]').click();   await expect(newLinkModal).toBeHidden();   const textarea = page.locator('textarea[name=content]');  await expect(textarea).toHaveValue(`[${description}](${url})`);  await save_visual(page);}); test('text expander has higher prio then prefix continuation', async ({page}) => {  const response = await page.goto('/user2/repo1/issues/new');  expect(response?.status()).toBe(200);   const textarea = page.locator('textarea[name=content]');  const initText = `* first`;  await textarea.fill(initText);  await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('rst'), it.value.indexOf('rst')));  await textarea.press('End');   // Test emoji completion  await textarea.press('Enter');  await textarea.pressSequentially(':smile_c');  await textarea.press('Enter');  await expect(textarea).toHaveValue(`* first\n* 😸`);   // Test username completion  await textarea.press('Enter');  await textarea.pressSequentially('@user');  await textarea.press('Enter');  await expect(textarea).toHaveValue(`* first\n* 😸\n* @user2 `);   await textarea.press('Enter');  await expect(textarea).toHaveValue(`* first\n* 😸\n* @user2 \n* `);}); test('Combo Markdown: preview mode switch', async ({page}) => {  // Load page with editor  const response = await page.goto('/user2/repo1/issues/new');  expect(response?.status()).toBe(200);   const toolbarItem = page.locator('md-header');  const editorPanel = page.locator('[data-tab-panel="markdown-writer"]');  const previewPanel = page.locator('[data-tab-panel="markdown-previewer"]');   // Verify correct visibility of related UI elements  await expect(toolbarItem).toBeVisible();  await expect(editorPanel).toBeVisible();  await expect(previewPanel).toBeHidden();   // Fill some content  const textarea = page.locator('textarea.markdown-text-editor');  await textarea.fill('**Content** :100: _100_');   // Switch to preview mode  await page.locator('a[data-tab-for="markdown-previewer"]').click();   // Verify that the related UI elements were switched correctly  await expect(toolbarItem).toBeHidden();  await expect(editorPanel).toBeHidden();  await expect(previewPanel).toBeVisible();  await save_visual(page);   // Verify that some content rendered  await expect(page.locator('[data-tab-panel="markdown-previewer"] .emoji[data-alias="100"]')).toBeVisible();   // Switch back to edit mode  await page.locator('a[data-tab-for="markdown-writer"]').click();   // Verify that the related UI elements were switched back correctly  await expect(toolbarItem).toBeVisible();  await expect(editorPanel).toBeVisible();  await expect(previewPanel).toBeHidden();  await save_visual(page);});