import type { InlineEditor } from '@inline/index.js';

import {
  assertSelection,
  enterInlineEditorPlayground,
  focusInlineRichText,
  getDeltaFromInlineRichText,
  getInlineRangeIndexRect,
  getInlineRichTextLine,
  press,
  setInlineRichTextRange,
  type,
} from '@inline/__tests__/utils.js';
import { ZERO_WIDTH_SPACE } from '@inline/consts.js';
import { expect, test } from '@playwright/test';

test('basic input', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorB = page.locator('[data-v-root="true"]').nth(1);

  const editorAUndo = page.getByText('undo').nth(0);
  const editorARedo = page.getByText('redo').nth(0);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);

  await type(page, 'abcd😃efg👨‍👨‍👧‍👦hj');

  expect(await editorA.innerText()).toBe('abcd😃efg👨‍👨‍👧‍👦hj');
  expect(await editorB.innerText()).toBe('abcd😃efg👨‍👨‍👧‍👦hj');

  await editorAUndo.click();

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await editorARedo.click();

  expect(await editorA.innerText()).toBe('abcd😃efg👨‍👨‍👧‍👦hj');
  expect(await editorB.innerText()).toBe('abcd😃efg👨‍👨‍👧‍👦hj');

  await focusInlineRichText(page);
  await press(page, 'Backspace');
  await press(page, 'Backspace');
  await press(page, 'Backspace');

  expect(await editorA.innerText()).toBe('abcd😃efg');
  expect(await editorB.innerText()).toBe('abcd😃efg');

  await editorAUndo.click();

  expect(await editorA.innerText()).toBe('abcd😃efg👨‍👨‍👧‍👦hj');
  expect(await editorB.innerText()).toBe('abcd😃efg👨‍👨‍👧‍👦hj');

  await editorARedo.click();

  expect(await editorA.innerText()).toBe('abcd😃efg');
  expect(await editorB.innerText()).toBe('abcd😃efg');

  await focusInlineRichText(page);
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'Delete');
  await press(page, 'Delete');

  await type(page, '🥰👨‍👨‍👧‍👦');
  expect(await editorA.innerText()).toBe('abc🥰👨‍👨‍👧‍👦efg');
  expect(await editorB.innerText()).toBe('abc🥰👨‍👨‍👧‍👦efg');

  await setInlineRichTextRange(page, {
    index: 3,
    length: 16,
  });
  await page.waitForTimeout(100);
  await press(page, 'Delete');

  expect(await editorA.innerText()).toBe('abc');
  expect(await editorA.innerText()).toBe('abc');

  await editorAUndo.click();

  expect(await editorA.innerText()).toBe('abcd😃efg');
  expect(await editorB.innerText()).toBe('abcd😃efg');

  await editorARedo.click();

  expect(await editorA.innerText()).toBe('abc');
  expect(await editorB.innerText()).toBe('abc');

  await focusInlineRichText(page);
  await page.waitForTimeout(100);
  await press(page, 'Enter');
  await press(page, 'Enter');
  await type(page, 'bbb');

  await page.waitForTimeout(100);

  expect(await editorA.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
  expect(await editorB.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');

  await editorAUndo.click();

  expect(await editorA.innerText()).toBe('abc');
  expect(await editorB.innerText()).toBe('abc');

  await editorARedo.click();

  expect(await editorA.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
  expect(await editorB.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');

  await focusInlineRichText(page);
  await page.waitForTimeout(100);
  await press(page, 'Backspace');
  await press(page, 'Backspace');
  await press(page, 'Backspace');
  await press(page, 'Backspace');
  await press(page, 'Backspace');

  expect(await editorA.innerText()).toBe('abc');
  expect(await editorB.innerText()).toBe('abc');

  await editorAUndo.click();

  expect(await editorA.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
  expect(await editorB.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');

  await editorARedo.click();

  expect(await editorA.innerText()).toBe('abc');

  await focusInlineRichText(page);
  await page.waitForTimeout(100);
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await type(page, 'bb');
  await press(page, 'ArrowRight');
  await press(page, 'ArrowRight');
  await type(page, 'dd');

  expect(await editorA.innerText()).toBe('abbbcdd');
  expect(await editorB.innerText()).toBe('abbbcdd');

  await editorAUndo.click();

  expect(await editorA.innerText()).toBe('abc');

  await editorARedo.click();

  expect(await editorA.innerText()).toBe('abbbcdd');
  expect(await editorB.innerText()).toBe('abbbcdd');

  await focusInlineRichText(page);
  await page.waitForTimeout(100);
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'Enter');
  await press(page, 'Enter');

  expect(await editorA.innerText()).toBe('abbbc\n' + ZERO_WIDTH_SPACE + '\ndd');
  expect(await editorB.innerText()).toBe('abbbc\n' + ZERO_WIDTH_SPACE + '\ndd');

  await editorAUndo.click();

  expect(await editorA.innerText()).toBe('abbbcdd');
  expect(await editorB.innerText()).toBe('abbbcdd');

  await editorARedo.click();

  expect(await editorA.innerText()).toBe('abbbc\n' + ZERO_WIDTH_SPACE + '\ndd');
  expect(await editorB.innerText()).toBe('abbbc\n' + ZERO_WIDTH_SPACE + '\ndd');
});

test('chinese input', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorB = page.locator('[data-v-root="true"]').nth(1);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);
  const client = await page.context().newCDPSession(page);
  await client.send('Input.imeSetComposition', {
    selectionStart: 0,
    selectionEnd: 0,
    text: 'n',
  });
  await client.send('Input.imeSetComposition', {
    selectionStart: 0,
    selectionEnd: 1,
    text: 'ni',
  });
  await client.send('Input.insertText', {
    text: '你',
  });
  expect(await editorA.innerText()).toBe('你');
  expect(await editorB.innerText()).toBe('你');
});

test('type many times in one moment', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);
  await page.waitForTimeout(100);
  await Promise.all(
    'aaaaaaaaaaaaaaaaaaaa'.split('').map(s => page.keyboard.type(s))
  );
  const preOffset = await page.evaluate(() => {
    return getSelection()?.getRangeAt(0).endOffset;
  });
  await page.keyboard.press('ArrowRight');
  await page.keyboard.press('ArrowRight');
  await page.keyboard.press('ArrowRight');
  const offset = await page.evaluate(() => {
    return getSelection()?.getRangeAt(0).endOffset;
  });
  expect(preOffset).toBe(offset);
});

test('readonly mode', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorB = page.locator('[data-v-root="true"]').nth(1);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);

  await type(page, 'abcdefg');

  expect(await editorA.innerText()).toBe('abcdefg');
  expect(await editorB.innerText()).toBe('abcdefg');

  await page.evaluate(() => {
    const richTextA = document
      .querySelector('test-page')
      ?.querySelector('test-rich-text');

    if (!richTextA) {
      throw new Error('Cannot find editor');
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    (richTextA as any).inlineEditor.setReadonly(true);
  });

  await type(page, 'aaaa');

  expect(await editorA.innerText()).toBe('abcdefg');
  expect(await editorB.innerText()).toBe('abcdefg');
});

test('basic styles', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorB = page.locator('[data-v-root="true"]').nth(1);

  const editorABold = page.getByText('bold').nth(0);
  const editorAItalic = page.getByText('italic').nth(0);
  const editorAUnderline = page.getByText('underline').nth(0);
  const editorAStrike = page.getByText('strike').nth(0);
  const editorACode = page.getByText('code').nth(0);

  const editorAUndo = page.getByText('undo').nth(0);
  const editorARedo = page.getByText('redo').nth(0);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);

  await type(page, 'abcdefg');

  expect(await editorA.innerText()).toBe('abcdefg');
  expect(await editorB.innerText()).toBe('abcdefg');

  let delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'abcdefg',
    },
  ]);

  await setInlineRichTextRange(page, { index: 2, length: 3 });

  await editorABold.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorAItalic.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        bold: true,
        italic: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorAUnderline.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        bold: true,
        italic: true,
        underline: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorAStrike.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        bold: true,
        italic: true,
        underline: true,
        strike: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorACode.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        bold: true,
        italic: true,
        underline: true,
        strike: true,
        code: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorAUndo.click({
    clickCount: 5,
  });
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'abcdefg',
    },
  ]);

  await editorARedo.click({
    clickCount: 5,
  });
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        bold: true,
        italic: true,
        underline: true,
        strike: true,
        code: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorABold.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        italic: true,
        underline: true,
        strike: true,
        code: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorAItalic.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        underline: true,
        strike: true,
        code: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorAUnderline.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        strike: true,
        code: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorAStrike.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'ab',
    },
    {
      insert: 'cde',
      attributes: {
        code: true,
      },
    },
    {
      insert: 'fg',
    },
  ]);

  await editorACode.click();
  await page.waitForTimeout(100);
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'abcdefg',
    },
  ]);
});

test('overlapping styles', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorB = page.locator('[data-v-root="true"]').nth(1);

  const editorABold = page.getByText('bold').nth(0);
  const editorAItalic = page.getByText('italic').nth(0);

  const editorAUndo = page.getByText('undo').nth(0);
  const editorARedo = page.getByText('redo').nth(0);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);

  await type(page, 'abcdefghijk');

  expect(await editorA.innerText()).toBe('abcdefghijk');
  expect(await editorB.innerText()).toBe('abcdefghijk');

  let delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'abcdefghijk',
    },
  ]);

  await setInlineRichTextRange(page, { index: 1, length: 3 });
  await editorABold.click();

  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'a',
    },
    {
      insert: 'bcd',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'efghijk',
    },
  ]);

  await setInlineRichTextRange(page, { index: 7, length: 3 });
  await editorABold.click();

  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'a',
    },
    {
      insert: 'bcd',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'efg',
    },
    {
      insert: 'hij',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'k',
    },
  ]);

  await setInlineRichTextRange(page, { index: 3, length: 5 });
  await editorAItalic.click();

  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'a',
    },
    {
      insert: 'bc',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'd',
      attributes: {
        bold: true,
        italic: true,
      },
    },
    {
      insert: 'efg',
      attributes: {
        italic: true,
      },
    },
    {
      insert: 'h',
      attributes: {
        bold: true,
        italic: true,
      },
    },
    {
      insert: 'ij',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'k',
    },
  ]);

  await editorAUndo.click({
    clickCount: 3,
  });
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'abcdefghijk',
    },
  ]);

  await editorARedo.click({
    clickCount: 3,
  });
  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'a',
    },
    {
      insert: 'bc',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'd',
      attributes: {
        bold: true,
        italic: true,
      },
    },
    {
      insert: 'efg',
      attributes: {
        italic: true,
      },
    },
    {
      insert: 'h',
      attributes: {
        bold: true,
        italic: true,
      },
    },
    {
      insert: 'ij',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'k',
    },
  ]);
});

test('input continuous spaces', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorB = page.locator('[data-v-root="true"]').nth(1);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);

  await type(page, 'abc   def');

  expect(await editorA.innerText()).toBe('abc   def');
  expect(await editorB.innerText()).toBe('abc   def');

  await focusInlineRichText(page);
  await page.waitForTimeout(100);
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');

  await press(page, 'Enter');

  expect(await editorA.innerText()).toBe('abc  \n' + ' def');
  expect(await editorB.innerText()).toBe('abc  \n' + ' def');
});

test('select from the start of line using shift+arrow', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorB = page.locator('[data-v-root="true"]').nth(1);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);

  await type(page, 'abc');
  await press(page, 'Enter');
  await type(page, 'def');
  await press(page, 'Enter');
  await type(page, 'ghi');

  expect(await editorA.innerText()).toBe('abc\ndef\nghi');
  expect(await editorB.innerText()).toBe('abc\ndef\nghi');

  /**
   * abc
   * def
   * |ghi
   */
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await assertSelection(page, 0, 8);

  /**
   * |abc
   * def
   * |ghi
   */
  await page.keyboard.down('Shift');
  await press(page, 'ArrowUp');
  await press(page, 'ArrowUp');
  await assertSelection(page, 0, 0, 8);

  /**
   * a|bc
   * def
   * |ghi
   */
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 1, 7);
  await press(page, 'Backspace');
  await page.waitForTimeout(100);

  expect(await editorA.innerText()).toBe('aghi');
  expect(await editorB.innerText()).toBe('aghi');
});

test('getLine', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorB = page.locator('[data-v-root="true"]').nth(1);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);

  await type(page, 'abc\ndef\nghi');

  expect(await editorA.innerText()).toBe('abc\ndef\nghi');
  expect(await editorB.innerText()).toBe('abc\ndef\nghi');

  const [line1, offset1] = await getInlineRichTextLine(page, 0);
  const [line2, offset2] = await getInlineRichTextLine(page, 1);
  const [line3, offset3] = await getInlineRichTextLine(page, 4);
  const [line4, offset4] = await getInlineRichTextLine(page, 5);
  const [line5, offset5] = await getInlineRichTextLine(page, 8);
  const [line6, offset6] = await getInlineRichTextLine(page, 11);

  expect(line1).toEqual('abc');
  expect(offset1).toEqual(0);
  expect(line2).toEqual('abc');
  expect(offset2).toEqual(1);
  expect(line3).toEqual('def');
  expect(offset3).toEqual(0);
  expect(line4).toEqual('def');
  expect(offset4).toEqual(1);
  expect(line5).toEqual('ghi');
  expect(offset5).toEqual(0);
  expect(line6).toEqual('ghi');
  expect(offset6).toEqual(3);
});

test('yText should not contain \r', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  await page.waitForTimeout(100);
  const message = await page.evaluate(() => {
    const richText = document
      .querySelector('test-page')
      ?.querySelector('test-rich-text');

    if (!richText) {
      throw new Error('Cannot find test-rich-text');
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const editor = (richText as any).inlineEditor as InlineEditor;

    try {
      editor.insertText({ index: 0, length: 0 }, 'abc\r');
    } catch (e) {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      return (e as any).message;
    }
  });

  expect(message).toBe(
    'yText must not contain "\\r" because it will break the range synchronization'
  );
});

test('embed', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorAEmbed = page.getByText('embed').nth(0);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);

  await page.waitForTimeout(100);

  await type(page, 'abcde');

  expect(await editorA.innerText()).toBe('abcde');

  await press(page, 'ArrowLeft');
  await page.waitForTimeout(100);
  await page.keyboard.down('Shift');
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await press(page, 'ArrowLeft');
  await page.keyboard.up('Shift');
  await page.waitForTimeout(100);
  await assertSelection(page, 0, 1, 3);

  await editorAEmbed.click();
  const embedCount = await page.locator('[data-v-embed="true"]').count();
  expect(embedCount).toBe(3);

  // try to update cursor position using arrow keys
  await assertSelection(page, 0, 1, 3);
  await press(page, 'ArrowLeft');
  await assertSelection(page, 0, 1, 0);
  await press(page, 'ArrowLeft');
  await assertSelection(page, 0, 0, 0);
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 1, 0);
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 1, 1);
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 2, 0);
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 2, 1);
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 3, 0);
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 3, 1);
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 4, 0);
  await press(page, 'ArrowRight');
  await assertSelection(page, 0, 5, 0);
  await press(page, 'ArrowLeft');
  await assertSelection(page, 0, 4, 0);
  await press(page, 'ArrowLeft');
  await assertSelection(page, 0, 3, 1);

  // try to update cursor position and select embed element by clicking embed element
  let rect = await getInlineRangeIndexRect(page, [0, 1]);
  await page.mouse.click(rect.x + 3, rect.y);
  await assertSelection(page, 0, 1, 1);

  rect = await getInlineRangeIndexRect(page, [0, 2]);
  await page.mouse.click(rect.x + 3, rect.y);
  await assertSelection(page, 0, 2, 1);

  rect = await getInlineRangeIndexRect(page, [0, 3]);
  await page.mouse.click(rect.x + 3, rect.y);
  await assertSelection(page, 0, 3, 1);
});

test('delete embed when pressing backspace after embed', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);
  const editorAEmbed = page.getByText('embed').nth(0);
  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  await page.waitForTimeout(100);
  await type(page, 'ab');
  expect(await editorA.innerText()).toBe('ab');

  await page.keyboard.down('Shift');
  await press(page, 'ArrowLeft');
  await page.keyboard.up('Shift');
  await page.waitForTimeout(100);
  await assertSelection(page, 0, 1, 1);
  await editorAEmbed.click();

  let delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'a',
    },
    {
      insert: 'b',
      attributes: {
        embed: true,
      },
    },
  ]);

  const rect = await getInlineRangeIndexRect(page, [0, 2]);
  // use click to select right side of the embed instead of use arrow key
  await page.mouse.click(rect.x + 3, rect.y);
  await assertSelection(page, 0, 2, 0);
  await press(page, 'Backspace');

  delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'a',
    },
  ]);
});

test('markdown shortcut using keyboard util', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  await page.waitForTimeout(100);

  await type(page, 'aaa**bbb** ccc');

  const delta = await getDeltaFromInlineRichText(page);
  expect(delta).toEqual([
    {
      insert: 'aaa',
    },
    {
      insert: 'bbb',
      attributes: {
        bold: true,
      },
    },
    {
      insert: 'ccc',
    },
  ]);
});

test('triple click to select line', async ({ page }) => {
  await enterInlineEditorPlayground(page);
  await focusInlineRichText(page);

  const editorA = page.locator('[data-v-root="true"]').nth(0);

  expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
  await page.waitForTimeout(100);
  await type(page, 'abc\nabc abc abc\nabc');

  expect(await editorA.innerText()).toBe('abc\nabc abc abc\nabc');

  const rect = await getInlineRangeIndexRect(page, [0, 10]);
  await page.mouse.click(rect.x, rect.y, {
    clickCount: 3,
  });
  await assertSelection(page, 0, 4, 11);

  await press(page, 'Backspace');
  expect(await editorA.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nabc');
});
