Skip to content

Commit

Permalink
enhance(E2E): editor string formatting and symbols autopairing tests (l…
Browse files Browse the repository at this point in the history
…ogseq#9388)

* enhance: only autopair tilda when text is selected

* Enhance/editor string formatting e2e (logseq#48)

* enhance(E2E): Test editor italic, bold, strikethrough and underline.

...

---------

Signed-off-by: Bad3r <[email protected]>
  • Loading branch information
Bad3r authored May 24, 2023
1 parent 82cf4d3 commit 29b4936
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 1 deletion.
200 changes: 199 additions & 1 deletion e2e-tests/editor.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { expect } from '@playwright/test'
import { test } from './fixtures'
import { createRandomPage, enterNextBlock, modKey } from './utils'
import {
createRandomPage,
enterNextBlock,
modKey,
repeatKeyPress,
moveCursor,
selectCharacters,
getSelection,
getCursorPos,
} from './utils'
import { dispatch_kb_events } from './util/keyboard-events'
import * as kb_events from './util/keyboard-events'

Expand Down Expand Up @@ -619,3 +628,192 @@ test('should keep correct undo and redo seq after indenting or outdenting the bl
await page.keyboard.press(modKey + '+Shift+z')
await expect(page.locator('textarea >> nth=0')).toHaveText("aaa bbb")
})

test.describe('Text Formatting', () => {
const formats = [
{ name: 'bold', prefix: '**', postfix: '**', shortcut: modKey + '+b' },
{ name: 'italic', prefix: '*', postfix: '*', shortcut: modKey + '+i' },
{
name: 'strikethrough',
prefix: '~~',
postfix: '~~',
shortcut: modKey + '+Shift+s',
},
// {
// name: 'underline',
// prefix: '<u>',
// postfix: '</u>',
// shortcut: modKey + '+u',
// },
]

for (const format of formats) {
test.describe(`${format.name} formatting`, () => {
test('Applying to an empty selection inserts placeholder formatting and places cursor correctly', async ({
page,
block,
}) => {
await createRandomPage(page)

const text = 'Lorem ipsum'
await block.mustFill(text)

// move the cursor to the end of Lorem
await repeatKeyPress(page, 'ArrowLeft', text.length - 'ipsum'.length)
await page.keyboard.press('Space')

// Apply formatting
await page.keyboard.press(format.shortcut)

await expect(page.locator('textarea >> nth=0')).toHaveText(
`Lorem ${format.prefix}${format.postfix} ipsum`
)

// Verify cursor position
const cursorPos = await getCursorPos(page)
expect(cursorPos).toBe(' ipsum'.length + format.prefix.length)
})

test('Applying to an entire block encloses the block in formatting and places cursor correctly', async ({
page,
block,
}) => {
await createRandomPage(page)

const text = 'Lorem ipsum-dolor sit.'
await block.mustFill(text)

// Select the entire block
await page.keyboard.press(modKey + '+a')

// Apply formatting
await page.keyboard.press(format.shortcut)

await expect(page.locator('textarea >> nth=0')).toHaveText(
`${format.prefix}${text}${format.postfix}`
)

// Verify cursor position
const cursorPosition = await getCursorPos(page)
expect(cursorPosition).toBe(format.prefix.length + text.length)
})

test('Applying and then removing from a word connected with a special character correctly formats and then reverts', async ({
page,
block,
}) => {
await createRandomPage(page)

await block.mustFill('Lorem ipsum-dolor sit.')

// Select 'ipsum'
// Move the cursor to the desired position
await moveCursor(page, -16)

// Select the desired length of text
await selectCharacters(page, 5)

// Apply formatting
await page.keyboard.press(format.shortcut)

// Verify that 'ipsum' is formatted
await expect(page.locator('textarea >> nth=0')).toHaveText(
`Lorem ${format.prefix}ipsum${format.postfix}-dolor sit.`
)

// Re-select 'ipsum'
// Move the cursor to the desired position
await moveCursor(page, -5)

// Select the desired length of text
await selectCharacters(page, 5)

// Remove formatting
await page.keyboard.press(format.shortcut)
await expect(page.locator('textarea >> nth=0')).toHaveText(
'Lorem ipsum-dolor sit.'
)

// Verify the word 'ipsum' is still selected
const selection = await getSelection(page)
expect(selection).toBe('ipsum')
})
})
}
})

test.describe('Always auto-pair symbols', () => {
// Define the symbols that should be auto-paired
const autoPairSymbols = [
{ name: 'square brackets', prefix: '[', postfix: ']' },
{ name: 'curly brackets', prefix: '{', postfix: '}' },
{ name: 'parentheses', prefix: '(', postfix: ')' },
// { name: 'angle brackets', prefix: '<', postfix: '>' },
{ name: 'backtick', prefix: '`', postfix: '`' },
// { name: 'single quote', prefix: "'", postfix: "'" },
// { name: 'double quote', prefix: '"', postfix: '"' },
]

for (const symbol of autoPairSymbols) {
test(`${symbol.name} auto-pairing`, async ({ page }) => {
await createRandomPage(page)

// Type prefix and check that the postfix is automatically added
page.type('textarea >> nth=0', symbol.prefix, { delay: 100 })
await expect(page.locator('textarea >> nth=0')).toHaveText(
`${symbol.prefix}${symbol.postfix}`
)

// Check that the cursor is positioned correctly between the prefix and postfix
const CursorPos = await getCursorPos(page)
expect(CursorPos).toBe(symbol.prefix.length)
})
}
})

test.describe('Auto-pair symbols only with text selection', () => {
const autoPairSymbols = [
// { name: 'tilde', prefix: '~', postfix: '~' },
{ name: 'asterisk', prefix: '*', postfix: '*' },
{ name: 'underscore', prefix: '_', postfix: '_' },
{ name: 'caret', prefix: '^', postfix: '^' },
{ name: 'equal', prefix: '=', postfix: '=' },
{ name: 'slash', prefix: '/', postfix: '/' },
{ name: 'plus', prefix: '+', postfix: '+' },
]

for (const symbol of autoPairSymbols) {
test(`Only auto-pair ${symbol.name} with text selection`, async ({
page,
block,
}) => {
await createRandomPage(page)

// type the symbol
page.type('textarea >> nth=0', symbol.prefix, { delay: 100 })

// Verify that there is no auto-pairing
await expect(page.locator('textarea >> nth=0')).toHaveText(symbol.prefix)

// remove prefix
await page.keyboard.press('Backspace')

// add text
await block.mustType('Lorem')
// select text
await page.keyboard.press(modKey + '+a')

// Type the prefix
await page.type('textarea >> nth=0', symbol.prefix, { delay: 100 })

// Verify that an additional postfix was automatically added around 'Lorem'
await expect(page.locator('textarea >> nth=0')).toHaveText(
`${symbol.prefix}Lorem${symbol.postfix}`
)

// Verify 'Lorem' is selected
const selection = await getSelection(page)
expect(selection).toBe('Lorem')
})
}
})
65 changes: 65 additions & 0 deletions e2e-tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,68 @@ export async function navigateToStartOfBlock(page: Page, block: Block) {
await page.keyboard.press('ArrowLeft')
}
}

/**
* Repeats a key press a certain number of times.
* @param {Page} page - The Page object.
* @param {string} key - The key to press.
* @param {number} times - The number of times to press the key.
* @return {Promise<void>} - Promise which resolves when the key press repetition is done.
*/
export async function repeatKeyPress(page: Page, key: string, times: number): Promise<void> {
for (let i = 0; i < times; i++) {
await page.keyboard.press(key);
}
}

/**
* Moves the cursor a certain number of characters to the right (positive value) or left (negative value).
* @param {Page} page - The Page object.
* @param {number} shift - The number of characters to move the cursor. Positive moves to the right, negative to the left.
* @return {Promise<void>} - Promise which resolves when the cursor has moved.
*/
export async function moveCursor(page: Page, shift: number): Promise<void> {
const direction = shift < 0 ? 'ArrowLeft' : 'ArrowRight';
const absShift = Math.abs(shift);
await repeatKeyPress(page, direction, absShift);
}

/**
* Selects a certain length of text in a textarea to the right of the cursor.
* @param {Page} page - The Page object.
* @param {number} length - The number of characters to select.
* @return {Promise<void>} - Promise which resolves when the text selection is done.
*/
export async function selectCharacters(page: Page, length: number): Promise<void> {
await page.keyboard.down('Shift');
await repeatKeyPress(page, 'ArrowRight', length);
await page.keyboard.up('Shift');
}

/**
* Retrieves the selected text in a textarea.
* @param {Page} page - The page object.
* @return {Promise<string | null>} - Promise which resolves to the selected text or null.
*/
export async function getSelection(page: Page): Promise<string | null> {
const selection = await page.evaluate(() => {
const textarea = document.querySelector('textarea')
return textarea?.value.substring(textarea.selectionStart, textarea.selectionEnd) || null
})

return selection
}

/**
* Retrieves the current cursor position in a textarea.
* @param {Page} page - The page object.
* @return {Promise<number | null>} - Promise which resolves to the cursor position or null.
*/
export async function getCursorPos(page: Page): Promise<number | null> {
const cursorPosition = await page.evaluate(() => {
const textarea = document.querySelector('textarea');
return textarea ? textarea.selectionStart : null;
});

return cursorPosition;
}

0 comments on commit 29b4936

Please sign in to comment.