loading up the forgejo repo on tangled to test page performance
at forgejo 13 kB view raw
1// @watch start 2// web_src/js/features/comp/ComboMarkdownEditor.js 3// web_src/css/editor/combomarkdowneditor.css 4// templates/shared/combomarkdowneditor.tmpl 5// @watch end 6 7import {expect} from '@playwright/test'; 8import {accessibilityCheck} from './shared/accessibility.ts'; 9import {save_visual, test} from './utils_e2e.ts'; 10 11test.use({user: 'user2'}); 12 13test('Markdown image preview behaviour', async ({page}, workerInfo) => { 14 test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari;'); 15 16 // Editing the root README.md file for image preview 17 const editPath = '/user2/repo1/src/branch/master/README.md'; 18 19 const response = await page.goto(editPath, {waitUntil: 'domcontentloaded'}); 20 expect(response?.status()).toBe(200); 21 22 // Click 'Edit file' tab 23 await page.locator('[data-tooltip-content="Edit file"]').click(); 24 25 // This yields the monaco editor 26 const editor = page.getByRole('presentation').nth(0); 27 await editor.click(); 28 // Clear all the content 29 await page.keyboard.press('ControlOrMeta+KeyA'); 30 // Add the image 31 await page.keyboard.type('![Logo of Forgejo](./assets/logo.svg "Logo of Forgejo")'); 32 33 // Click 'Preview' tab 34 await page.locator('a[data-tab="preview"]').click(); 35 36 // Check for the image preview via the expected attribute 37 const preview = page.locator('div[data-tab="preview"] p[dir="auto"] a'); 38 await expect(preview).toHaveAttribute('href', 'http://localhost:3003/user2/repo1/media/branch/master/assets/logo.svg'); 39 await save_visual(page); 40}); 41 42test('Markdown indentation', async ({page}) => { 43 const initText = `* first\n* second\n* third\n* last`; 44 45 const response = await page.goto('/user2/repo1/issues/new'); 46 expect(response?.status()).toBe(200); 47 48 const textarea = page.locator('textarea[name=content]'); 49 const tab = ' '; 50 const indent = page.locator('button[data-md-action="indent"]'); 51 const unindent = page.locator('button[data-md-action="unindent"]'); 52 await textarea.fill(initText); 53 await textarea.click(); // Tab handling is disabled until pointer event or input. 54 55 // Indent, then unindent first line 56 await textarea.focus(); 57 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(0, 0)); 58 await indent.click(); 59 await expect(textarea).toHaveValue(`${tab}* first\n* second\n* third\n* last`); 60 await unindent.click(); 61 await expect(textarea).toHaveValue(initText); 62 63 // Indent second line while somewhere inside of it 64 await textarea.focus(); 65 await textarea.press('ArrowDown'); 66 await textarea.press('ArrowRight'); 67 await textarea.press('ArrowRight'); 68 await indent.click(); 69 await expect(textarea).toHaveValue(`* first\n${tab}* second\n* third\n* last`); 70 71 // Subsequently, select a chunk of 2nd and 3rd line and indent both, preserving the cursor position in relation to text 72 await textarea.focus(); 73 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('hird'))); 74 await indent.click(); 75 const lines23 = `* first\n${tab}${tab}* second\n${tab}* third\n* last`; 76 await expect(textarea).toHaveValue(lines23); 77 await expect(textarea).toHaveJSProperty('selectionStart', lines23.indexOf('cond')); 78 await expect(textarea).toHaveJSProperty('selectionEnd', lines23.indexOf('hird')); 79 80 // Then unindent twice, erasing all indents. 81 await unindent.click(); 82 await expect(textarea).toHaveValue(`* first\n${tab}* second\n* third\n* last`); 83 await unindent.click(); 84 await expect(textarea).toHaveValue(initText); 85 86 // Indent and unindent with cursor at the end of the line 87 await textarea.focus(); 88 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('cond'))); 89 await textarea.press('End'); 90 await indent.click(); 91 await expect(textarea).toHaveValue(`* first\n${tab}* second\n* third\n* last`); 92 await unindent.click(); 93 await expect(textarea).toHaveValue(initText); 94 95 // Check that Tab does work after input 96 await textarea.focus(); 97 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); 98 await textarea.press('Shift+Enter'); // Avoid triggering the prefix continuation feature 99 await textarea.pressSequentially('* least'); 100 await indent.click(); 101 await expect(textarea).toHaveValue(`* first\n* second\n* third\n* last\n${tab}* least`); 102 103 // Check that partial indents are cleared 104 await textarea.focus(); 105 await textarea.fill(initText); 106 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('* second'), it.value.indexOf('* second'))); 107 await textarea.pressSequentially(' '); 108 await unindent.click(); 109 await expect(textarea).toHaveValue(initText); 110}); 111 112test('Markdown list continuation', async ({page}) => { 113 const initText = `* first\n* second`; 114 115 const response = await page.goto('/user2/repo1/issues/new'); 116 expect(response?.status()).toBe(200); 117 118 const textarea = page.locator('textarea[name=content]'); 119 const tab = ' '; 120 const indent = page.locator('button[data-md-action="indent"]'); 121 await textarea.fill(initText); 122 123 // Test continuation of ' * ' prefix 124 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('rst'), it.value.indexOf('rst'))); 125 await indent.click(); 126 await textarea.press('End'); 127 await textarea.press('Enter'); 128 await textarea.pressSequentially('muddle'); 129 await expect(textarea).toHaveValue(`${tab}* first\n${tab}* muddle\n* second`); 130 131 // Test breaking in the middle of a line 132 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.lastIndexOf('ddle'), it.value.lastIndexOf('ddle'))); 133 await textarea.pressSequentially('tate'); 134 await textarea.press('Enter'); 135 await textarea.pressSequentially('me'); 136 await expect(textarea).toHaveValue(`${tab}* first\n${tab}* mutate\n${tab}* meddle\n* second`); 137 138 // Test not triggering when Shift held 139 await textarea.fill(initText); 140 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); 141 await textarea.press('Shift+Enter'); 142 await textarea.press('Enter'); 143 await textarea.pressSequentially('...but not least'); 144 await expect(textarea).toHaveValue(`* first\n* second\n\n...but not least`); 145 146 // Test continuation of ordered list 147 await textarea.fill(`1. one`); 148 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); 149 await textarea.press('Enter'); 150 await textarea.pressSequentially(' '); 151 await textarea.press('Enter'); 152 await textarea.pressSequentially('three'); 153 await textarea.press('Enter'); 154 await textarea.press('Enter'); 155 await expect(textarea).toHaveValue(`1. one\n2. \n3. three\n\n`); 156 157 // Test continuation of alternative ordered list syntax 158 await textarea.fill(`1) one`); 159 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); 160 await textarea.press('Enter'); 161 await textarea.pressSequentially(' '); 162 await textarea.press('Enter'); 163 await textarea.pressSequentially('three'); 164 await textarea.press('Enter'); 165 await textarea.press('Enter'); 166 await expect(textarea).toHaveValue(`1) one\n2) \n3) three\n\n`); 167 168 // Test continuation of checklists 169 await textarea.fill(`- [ ]have a problem\n- [x]create a solution`); 170 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); 171 await textarea.press('Enter'); 172 await textarea.pressSequentially('write a test'); 173 await expect(textarea).toHaveValue(`- [ ]have a problem\n- [x]create a solution\n- [ ]write a test`); 174 175 // Test all conceivable syntax (except ordered lists) 176 const prefixes = [ 177 '- ', // A space between the bullet and the content is required. 178 ' - ', // I have seen single space in front of -/* being used and even recommended, I think. 179 '* ', 180 '+ ', 181 ' ', 182 ' ', 183 ' - ', 184 '\t', 185 '\t\t* ', 186 '> ', 187 '> > ', 188 '- [ ] ', 189 '* [ ] ', 190 '+ [ ] ', 191 ]; 192 for (const prefix of prefixes) { 193 await textarea.fill(`${prefix}one`); 194 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); 195 await textarea.press('Enter'); 196 await textarea.pressSequentially(' '); 197 await textarea.press('Enter'); 198 await textarea.pressSequentially('two'); 199 await textarea.press('Enter'); 200 await textarea.press('Enter'); 201 await expect(textarea).toHaveValue(`${prefix}one\n${prefix} \n${prefix}two\n\n`); 202 } 203}); 204 205test('Markdown insert table', async ({page}) => { 206 const response = await page.goto('/user2/repo1/issues/new'); 207 expect(response?.status()).toBe(200); 208 209 const newTableButton = page.locator('button[data-md-action="new-table"]'); 210 await newTableButton.click(); 211 212 const newTableModal = page.locator('div[data-markdown-table-modal-id="0"]'); 213 await expect(newTableModal).toBeVisible(); 214 await save_visual(page); 215 216 await newTableModal.locator('input[name="table-rows"]').fill('3'); 217 await newTableModal.locator('input[name="table-columns"]').fill('2'); 218 219 await newTableModal.locator('button[data-selector-name="ok-button"]').click(); 220 221 await expect(newTableModal).toBeHidden(); 222 223 const textarea = page.locator('textarea[name=content]'); 224 await expect(textarea).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n'); 225 await save_visual(page); 226}); 227 228test('Markdown insert link', async ({page}) => { 229 const response = await page.goto('/user2/repo1/issues/new'); 230 expect(response?.status()).toBe(200); 231 232 const newLinkButton = page.locator('button[data-md-action="new-link"]'); 233 await newLinkButton.click(); 234 235 const newLinkModal = page.locator('div[data-markdown-link-modal-id="0"]'); 236 await expect(newLinkModal).toBeVisible(); 237 await accessibilityCheck({page}, ['[data-modal-name="new-markdown-link"]'], [], []); 238 await save_visual(page); 239 240 const url = 'https://example.com'; 241 const description = 'Where does this lead?'; 242 243 await newLinkModal.locator('input[name="link-url"]').fill(url); 244 await newLinkModal.locator('input[name="link-description"]').fill(description); 245 246 await newLinkModal.locator('button[data-selector-name="ok-button"]').click(); 247 248 await expect(newLinkModal).toBeHidden(); 249 250 const textarea = page.locator('textarea[name=content]'); 251 await expect(textarea).toHaveValue(`[${description}](${url})`); 252 await save_visual(page); 253}); 254 255test('text expander has higher prio then prefix continuation', async ({page}) => { 256 const response = await page.goto('/user2/repo1/issues/new'); 257 expect(response?.status()).toBe(200); 258 259 const textarea = page.locator('textarea[name=content]'); 260 const initText = `* first`; 261 await textarea.fill(initText); 262 await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('rst'), it.value.indexOf('rst'))); 263 await textarea.press('End'); 264 265 // Test emoji completion 266 await textarea.press('Enter'); 267 await textarea.pressSequentially(':smile_c'); 268 await textarea.press('Enter'); 269 await expect(textarea).toHaveValue(`* first\n* 😸`); 270 271 // Test username completion 272 await textarea.press('Enter'); 273 await textarea.pressSequentially('@user'); 274 await textarea.press('Enter'); 275 await expect(textarea).toHaveValue(`* first\n* 😸\n* @user2 `); 276 277 await textarea.press('Enter'); 278 await expect(textarea).toHaveValue(`* first\n* 😸\n* @user2 \n* `); 279}); 280 281test('Combo Markdown: preview mode switch', async ({page}) => { 282 // Load page with editor 283 const response = await page.goto('/user2/repo1/issues/new'); 284 expect(response?.status()).toBe(200); 285 286 const toolbarItem = page.locator('md-header'); 287 const editorPanel = page.locator('[data-tab-panel="markdown-writer"]'); 288 const previewPanel = page.locator('[data-tab-panel="markdown-previewer"]'); 289 290 // Verify correct visibility of related UI elements 291 await expect(toolbarItem).toBeVisible(); 292 await expect(editorPanel).toBeVisible(); 293 await expect(previewPanel).toBeHidden(); 294 295 // Fill some content 296 const textarea = page.locator('textarea.markdown-text-editor'); 297 await textarea.fill('**Content** :100: _100_'); 298 299 // Switch to preview mode 300 await page.locator('a[data-tab-for="markdown-previewer"]').click(); 301 302 // Verify that the related UI elements were switched correctly 303 await expect(toolbarItem).toBeHidden(); 304 await expect(editorPanel).toBeHidden(); 305 await expect(previewPanel).toBeVisible(); 306 await save_visual(page); 307 308 // Verify that some content rendered 309 await expect(page.locator('[data-tab-panel="markdown-previewer"] .emoji[data-alias="100"]')).toBeVisible(); 310 311 // Switch back to edit mode 312 await page.locator('a[data-tab-for="markdown-writer"]').click(); 313 314 // Verify that the related UI elements were switched back correctly 315 await expect(toolbarItem).toBeVisible(); 316 await expect(editorPanel).toBeVisible(); 317 await expect(previewPanel).toBeHidden(); 318 await save_visual(page); 319});