mirror of
https://codeberg.org/forgejo/forgejo
synced 2025-10-19 02:30:52 +02:00
When you edit a comment and the comment already has a markdown editor, then the code will click on the 'Write' tab, in case you canceled editting the comment when you were at the 'Preview' tab. In forgejo/forgejo#2681 I added `href="#"` to the tab items, this causes that when the 'Write' tab is being clicked by the code the page is jumped the beginning of the page. Instead of being clever and trying to make this item interactive via another way or via javascript avoid this jumping, we do better and make this element a button. This item is not a link, it's a button that will perform a action. This entirely avoids the issue of jumping and it's still interactive. Resolves forgejo/forgejo#9542 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9645 Reviewed-by: Beowulf <beowulf@beocode.eu> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
566 lines
24 KiB
TypeScript
566 lines
24 KiB
TypeScript
// @watch start
|
|
// web_src/js/modules/tab.ts
|
|
// web_src/css/modules/tab.css
|
|
// 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 {test} from './utils_e2e.ts';
|
|
import {screenshot} from './shared/screenshots.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('');
|
|
|
|
// 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 screenshot(page);
|
|
});
|
|
|
|
test('Markdown indentation via toolbar', 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);
|
|
|
|
// 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 indentation with Tab', 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 toast = page.locator('.toastify');
|
|
const tab = ' ';
|
|
|
|
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 textarea.press('Tab');
|
|
await expect(textarea).toHaveValue(`${tab}* first\n* second\n* third\n* last`);
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).toHaveValue(initText);
|
|
|
|
// Attempt unindent again, ensure focus is not immediately lost and toast is shown, but then focus is lost on next attempt.
|
|
await expect(toast).toBeHidden(); // toast should not already be there
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).toBeFocused();
|
|
await expect(toast).toBeVisible();
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).not.toBeFocused();
|
|
|
|
// Indent lines 2-4
|
|
await textarea.click();
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('\n') + 1, it.value.length));
|
|
await textarea.press('Tab');
|
|
await expect(textarea).toHaveValue(`* first\n${tab}* second\n${tab}* third\n${tab}* last`);
|
|
|
|
// Indent second line while in whitespace, then unindent.
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf(' * third'), it.value.indexOf(' * third')));
|
|
await textarea.press('Tab');
|
|
await expect(textarea).toHaveValue(`* first\n${tab}* second\n${tab}${tab}* third\n${tab}* last`);
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).toHaveValue(`* first\n${tab}* second\n${tab}* third\n${tab}* last`);
|
|
|
|
// Select all and unindent, then lose focus.
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.select());
|
|
await textarea.press('Shift+Tab'); // Everything is unindented.
|
|
await expect(textarea).toHaveValue(initText);
|
|
await textarea.press('Shift+Tab'); // Valid, but nothing happens -> switch to "about to lose focus" state.
|
|
await expect(textarea).toBeFocused();
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).not.toBeFocused();
|
|
|
|
// Attempt the same with cursor within list element body.
|
|
await textarea.focus();
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(0, 0));
|
|
await textarea.press('ArrowRight');
|
|
await textarea.press('ArrowRight');
|
|
await textarea.press('Tab');
|
|
// Whole line should be indented.
|
|
await expect(textarea).toHaveValue(`${tab}* first\n* second\n* third\n* last`);
|
|
await textarea.press('Shift+Tab');
|
|
|
|
// Subsequently, select a chunk of 2nd and 3rd line and indent both, preserving the cursor position in relation to text
|
|
const line3 = `* first\n* second\n${tab}* third\n* last`;
|
|
const lines23 = `* first\n${tab}* second\n${tab}${tab}* third\n* last`;
|
|
await textarea.focus();
|
|
await textarea.fill(line3);
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('hird')));
|
|
await textarea.press('Tab');
|
|
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 textarea.press('Shift+Tab');
|
|
await expect(textarea).toHaveValue(line3);
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).toHaveValue(initText);
|
|
|
|
// 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 textarea.press('Shift+Tab');
|
|
await expect(textarea).toHaveValue(initText);
|
|
|
|
// Check that indentation tokens not at the start of the string do not interrupt indentation
|
|
await textarea.focus();
|
|
await textarea.fill(initText);
|
|
await textarea.pressSequentially(tab);
|
|
await textarea.press('Tab');
|
|
await expect(textarea).toHaveValue(`* first\n* second\n* third\n * last `);
|
|
});
|
|
|
|
test('markdown block quote 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 toast = page.locator('.toastify');
|
|
|
|
await textarea.fill(initText);
|
|
|
|
await textarea.click(); // Tab handling is disabled until pointer event or input.
|
|
|
|
// Indent, then unindent first line twice (quotes can quote quotes!)
|
|
await textarea.focus();
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(0, 0));
|
|
await textarea.press('Tab');
|
|
await expect(textarea).toHaveValue(`> > first\n> second\n> third\n> last`);
|
|
await textarea.press('Tab');
|
|
await expect(textarea).toHaveValue(`> > > first\n> second\n> third\n> last`);
|
|
await textarea.press('Shift+Tab');
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).toHaveValue(initText);
|
|
|
|
// Attempt unindent again.
|
|
await expect(toast).toBeHidden(); // toast should not already be there
|
|
await textarea.press('Shift+Tab');
|
|
// Nothing happens - quote should not stop being a quote
|
|
await expect(textarea).toHaveValue(initText);
|
|
// Focus is not immediately lost and toast is shown,
|
|
await expect(textarea).toBeFocused();
|
|
await expect(toast).toBeVisible();
|
|
// Focus is lost on next attempt,
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).not.toBeFocused();
|
|
|
|
// Indent lines 2-4
|
|
await textarea.click();
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('\n') + 1, it.value.length));
|
|
await textarea.press('Tab');
|
|
await expect(textarea).toHaveValue(`> first\n> > second\n> > third\n> > last`);
|
|
|
|
// Select all and unindent, then lose focus.
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.select());
|
|
await textarea.press('Shift+Tab'); // Everything is unindented.
|
|
await expect(textarea).toHaveValue(initText);
|
|
await textarea.press('Shift+Tab'); // Valid, but nothing happens -> switch to "about to lose focus" state.
|
|
await expect(textarea).toBeFocused();
|
|
await textarea.press('Shift+Tab');
|
|
await expect(textarea).not.toBeFocused();
|
|
});
|
|
|
|
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 screenshot(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 screenshot(page);
|
|
});
|
|
|
|
test('Markdown insert link', async ({page}, workerInfo) => {
|
|
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'Unreliable in this test');
|
|
|
|
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 screenshot(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 screenshot(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('[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 screenshot(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('[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();
|
|
|
|
// Validate switch height: it is customized to be same height as other buttons on the panel
|
|
expect(await page.locator('markdown-toolbar .switch').evaluate((el) => getComputedStyle(el).height)).toBe(await page.locator('md-header.markdown-toolbar-button').evaluate((el) => getComputedStyle(el).height));
|
|
|
|
await screenshot(page);
|
|
});
|
|
|
|
test('Multiple combo markdown: insert table', async ({page}) => {
|
|
const response = await page.goto('/user2/multiple-combo-boxes/issues/new?template=.forgejo%2fissue_template%2fmulti-combo-boxes.yaml');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
// check that there are two textareas
|
|
const textareaOne = page.locator('textarea[name=form-field-textarea-one]');
|
|
const comboboxOne = page.locator('textarea#_combo_markdown_editor_0');
|
|
await expect(textareaOne).toBeVisible();
|
|
await expect(comboboxOne).toBeHidden();
|
|
const textareaTwo = page.locator('textarea[name=form-field-textarea-two]');
|
|
const comboboxTwo = page.locator('textarea#_combo_markdown_editor_1');
|
|
await expect(textareaTwo).toBeVisible();
|
|
await expect(comboboxTwo).toBeHidden();
|
|
|
|
// focus first one and add table to it
|
|
await textareaOne.click();
|
|
await expect(comboboxOne).toBeVisible();
|
|
await expect(comboboxTwo).toBeHidden();
|
|
|
|
const newTableButtonOne = page.locator('[for="_combo_markdown_editor_0"] button[data-md-action="new-table"]');
|
|
await newTableButtonOne.click();
|
|
|
|
const newTableModalOne = page.locator('div[data-markdown-table-modal-id="0"]');
|
|
await expect(newTableModalOne).toBeVisible();
|
|
|
|
await newTableModalOne.locator('input[name="table-rows"]').fill('3');
|
|
await newTableModalOne.locator('input[name="table-columns"]').fill('2');
|
|
|
|
await newTableModalOne.locator('button[data-selector-name="ok-button"]').click();
|
|
|
|
await expect(newTableModalOne).toBeHidden();
|
|
|
|
await expect(comboboxOne).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n');
|
|
await expect(comboboxTwo).toBeEmpty();
|
|
await screenshot(page);
|
|
|
|
// focus second one and add table to it
|
|
await textareaTwo.click();
|
|
await expect(comboboxOne).toBeHidden();
|
|
await expect(comboboxTwo).toBeVisible();
|
|
|
|
const newTableButtonTwo = page.locator('[for="_combo_markdown_editor_1"] button[data-md-action="new-table"]');
|
|
await newTableButtonTwo.click();
|
|
|
|
const newTableModalTwo = page.locator('div[data-markdown-table-modal-id="1"]');
|
|
await expect(newTableModalTwo).toBeVisible();
|
|
|
|
await newTableModalTwo.locator('input[name="table-rows"]').fill('2');
|
|
await newTableModalTwo.locator('input[name="table-columns"]').fill('3');
|
|
|
|
await newTableModalTwo.locator('button[data-selector-name="ok-button"]').click();
|
|
|
|
await expect(newTableModalTwo).toBeHidden();
|
|
|
|
await expect(comboboxOne).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n');
|
|
await expect(comboboxTwo).toHaveValue('| Header | Header | Header |\n|---------|---------|---------|\n| Content | Content | Content |\n| Content | Content | Content |\n');
|
|
await screenshot(page);
|
|
});
|
|
|
|
test('Markdown bold/italic toolbar and shortcut', async ({page}) => {
|
|
const initText = `line 1\nline 2\nline 3\nline 4`;
|
|
|
|
const response = await page.goto('/user2/repo1/issues/new');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
const textarea = page.locator('textarea[name=content]');
|
|
await textarea.fill(initText);
|
|
await textarea.focus();
|
|
await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('line 1'), it.value.indexOf('line 2')));
|
|
|
|
// Cases: bold via toolbar, bold via shortcut, repeat w/ italics
|
|
page.locator('md-bold').click();
|
|
await expect(textarea).toHaveValue(`**line 1**\nline 2\nline 3\nline 4`);
|
|
page.locator('md-bold').click();
|
|
await expect(textarea).toHaveValue(`line 1\nline 2\nline 3\nline 4`);
|
|
|
|
await textarea.press('ControlOrMeta+KeyB');
|
|
await expect(textarea).toHaveValue(`**line 1**\nline 2\nline 3\nline 4`);
|
|
await textarea.press('ControlOrMeta+KeyB');
|
|
await expect(textarea).toHaveValue(`line 1\nline 2\nline 3\nline 4`);
|
|
|
|
page.locator('md-italic').click();
|
|
await expect(textarea).toHaveValue(`_line 1_\nline 2\nline 3\nline 4`);
|
|
page.locator('md-italic').click();
|
|
await expect(textarea).toHaveValue(`line 1\nline 2\nline 3\nline 4`);
|
|
|
|
await textarea.press('ControlOrMeta+KeyI');
|
|
await expect(textarea).toHaveValue(`_line 1_\nline 2\nline 3\nline 4`);
|
|
await textarea.press('ControlOrMeta+KeyI');
|
|
await expect(textarea).toHaveValue(`line 1\nline 2\nline 3\nline 4`);
|
|
});
|