-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Playwright tests #337
Add Playwright tests #337
Changes from 41 commits
669ffd0
9351ef7
6cfb39e
dcf45fd
850215d
12ac98d
c9fc4b8
72f12ef
8ef9df1
b2452bb
906b98b
abbaff1
f661556
bb58135
67a2e9d
124d151
690ddc1
ac548f7
1dd1977
e26e01f
afa5758
9275353
4286e8e
9d63b1c
d0e36b7
721b2ca
f12b729
e92e31b
9e1464f
7e28ce8
a702f31
94e22d6
9f4d9c3
3a065f2
5b06cd2
7e3e85a
4daf5aa
085d094
abc897a
94d7ddb
2764945
c44a707
a0a1bf2
72e700f
cd88a46
b04d1f1
3c6d829
d612c93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,49 @@ | ||||||
name: E2E Test | ||||||
on: | ||||||
pull_request: | ||||||
paths: | ||||||
- .github/workflows/e2e-test.yml | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's rename the file and change this line
Suggested change
|
||||||
- src/** | ||||||
- WebExample/** | ||||||
merge_group: | ||||||
branches: | ||||||
- main | ||||||
push: | ||||||
branches: | ||||||
- main | ||||||
paths: | ||||||
- .github/workflows/e2e-test.yml | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and here |
||||||
- src/** | ||||||
- WebExample/** | ||||||
|
||||||
jobs: | ||||||
e2e_test: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
if: github.repository == 'Expensify/react-native-live-markdown' | ||||||
runs-on: ubuntu-latest | ||||||
defaults: | ||||||
run: | ||||||
working-directory: ./WebExample | ||||||
|
||||||
concurrency: | ||||||
group: e2e-test-${{ github.ref }} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and here |
||||||
cancel-in-progress: true | ||||||
steps: | ||||||
- name: Check out Git repository | ||||||
uses: actions/checkout@v4 | ||||||
|
||||||
- name: Use Node.js 18 | ||||||
uses: actions/setup-node@v4 | ||||||
with: | ||||||
node-version: 18 | ||||||
|
||||||
- name: Install node_modules | ||||||
run: yarn install --immutable | ||||||
|
||||||
- name: Install browsers | ||||||
run: npx playwright install --with-deps | ||||||
|
||||||
- name: Install dependencies for browsers | ||||||
run: npx playwright install-deps | ||||||
|
||||||
- name: Run playwright tests | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
run: yarn test |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,3 +33,7 @@ yarn-error.* | |
|
||
# typescript | ||
*.tsbuildinfo | ||
/test-results/ | ||
/playwright-report/ | ||
/blob-report/ | ||
/playwright/.cache/ |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -6,7 +6,8 @@ | |||||
"start": "expo start", | ||||||
"android": "expo start --android", | ||||||
"ios": "expo start --ios", | ||||||
"web": "expo start --web" | ||||||
"web": "expo start --web", | ||||||
"test": "npx playwright test" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure if this would work There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please try |
||||||
}, | ||||||
"dependencies": { | ||||||
"@expo/webpack-config": "~19.0.1", | ||||||
|
@@ -20,6 +21,8 @@ | |||||
}, | ||||||
"devDependencies": { | ||||||
"@babel/core": "^7.20.0", | ||||||
"@playwright/test": "^1.43.1", | ||||||
"@types/node": "^20.12.7", | ||||||
"@types/react": "~18.2.45", | ||||||
"typescript": "^5.1.3" | ||||||
}, | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {defineConfig, devices} from '@playwright/test'; | ||
import * as TEST_CONST from '../testConstants'; | ||
|
||
export default defineConfig({ | ||
testDir: './tests', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we rename the directory to |
||
preserveOutput: 'never', | ||
outputDir: undefined, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need to set this prop? What's the default value? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the default is |
||
webServer: { | ||
command: 'yarn web', | ||
url: TEST_CONST.LOCAL_URL, | ||
reuseExistingServer: !process.env.CI, | ||
stdout: 'pipe', | ||
stderr: 'pipe', | ||
}, | ||
projects: [ | ||
{ | ||
name: 'Chromium', | ||
use: {...devices['Desktop Chrome']}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is directly from the documentation |
||
}, | ||
{ | ||
name: 'Firefox', | ||
use: {...devices['Desktop Firefox']}, | ||
}, | ||
{ | ||
name: 'Webkit', | ||
use: {...devices['Desktop Safari']}, | ||
}, | ||
], | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module.exports = { | ||
'rules': { | ||
'@lwc/lwc/no-async-await': 'off', | ||
'rulesdir/prefer-import-module-contents': 'off', | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing trailing newline |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,31 @@ | ||||||
import {test, expect} from '@playwright/test'; | ||||||
import * as TEST_CONST from '../../testConstants'; | ||||||
import {checkCursorPosition, setupInput} from './utils'; | ||||||
|
||||||
test.beforeEach(async ({page}) => { | ||||||
await page.goto(TEST_CONST.LOCAL_URL, {waitUntil: 'load'}); | ||||||
}); | ||||||
|
||||||
test.describe('standard input behaviour', () => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
test('standard input results', async ({page}) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
const inputLocator = await setupInput(page, 'clear'); | ||||||
|
||||||
await inputLocator.pressSequentially(TEST_CONST.EXAMPLE_CONTENT); | ||||||
const value = await inputLocator.innerText(); | ||||||
expect(value).toEqual(TEST_CONST.EXAMPLE_CONTENT); | ||||||
}); | ||||||
|
||||||
test('fast type cursor position', async ({page}) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not convinced - it doesn't describe this test properly. The main purpose was to verify if the cursor position would get desynchronized when typing fast (there was a bug with this behavior). It's not a standard long text input scenario. |
||||||
const EXAMPLE_LONG_CONTENT = TEST_CONST.EXAMPLE_CONTENT.repeat(3); | ||||||
|
||||||
const inputLocator = await setupInput(page, 'clear'); | ||||||
|
||||||
await inputLocator.pressSequentially(EXAMPLE_LONG_CONTENT); | ||||||
|
||||||
expect(await inputLocator.innerText()).toBe(EXAMPLE_LONG_CONTENT); | ||||||
|
||||||
const cursorPosition = await page.evaluate(checkCursorPosition); | ||||||
|
||||||
expect(cursorPosition).toBe(EXAMPLE_LONG_CONTENT.length); | ||||||
}); | ||||||
}); |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,66 @@ | ||||||
import {test, expect} from '@playwright/test'; | ||||||
import type {Page} from '@playwright/test'; | ||||||
import * as TEST_CONST from '../../testConstants'; | ||||||
import {setupInput} from './utils'; | ||||||
|
||||||
const testMarkdownContentStyle = async ({styleName, style, page}: {styleName: string; style: string; page: Page}) => { | ||||||
const inputLocator = await setupInput(page); | ||||||
|
||||||
const elementHandle = inputLocator.locator('span', {hasText: styleName}).last(); | ||||||
|
||||||
let elementStyle; | ||||||
|
||||||
if (elementHandle) { | ||||||
await elementHandle.waitFor({state: 'attached'}); | ||||||
|
||||||
elementStyle = await elementHandle.getAttribute('style'); | ||||||
} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to move it to an utility function? Looks like we use it again in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. k, I'll move it |
||||||
expect(elementStyle).toEqual(style); | ||||||
}; | ||||||
|
||||||
test.beforeEach(async ({page}) => { | ||||||
await page.goto(TEST_CONST.LOCAL_URL, {waitUntil: 'load'}); | ||||||
await page.click('[data-testid="reset"]'); | ||||||
}); | ||||||
|
||||||
test.describe('markdown content styling', () => { | ||||||
test('bold', async ({page}) => { | ||||||
await testMarkdownContentStyle({styleName: 'bold', style: TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.bold.style, page}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not a huge fan of This test should contain There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in terms of |
||||||
}); | ||||||
|
||||||
test('link', async ({page}) => { | ||||||
await testMarkdownContentStyle({styleName: 'link', style: TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.link.style, page}); | ||||||
}); | ||||||
|
||||||
test('title', async ({page}) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please make sure that the names of the tests are aligned with the rest of the codebase
Suggested change
|
||||||
await testMarkdownContentStyle({styleName: 'title', style: TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.title.style, page}); | ||||||
}); | ||||||
|
||||||
test('code', async ({page}) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
await testMarkdownContentStyle({styleName: 'code', style: TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.code.style, page}); | ||||||
}); | ||||||
|
||||||
test('codeBlock', async ({page}) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
await testMarkdownContentStyle({styleName: 'codeBlock', style: TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.codeBlock.style, page}); | ||||||
}); | ||||||
|
||||||
test('hereMention', async ({page}) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
await testMarkdownContentStyle({styleName: 'here', style: TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.here.style, page}); | ||||||
}); | ||||||
|
||||||
test('mentionUser', async ({page}) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
await testMarkdownContentStyle({styleName: 'mentionUser', style: TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.mentionUser.style, page}); | ||||||
}); | ||||||
|
||||||
test('roomMention', async ({page}) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
await testMarkdownContentStyle({styleName: 'roomMention', style: TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.roomMention.style, page}); | ||||||
}); | ||||||
|
||||||
test('blockquote', async ({page, browserName}) => { | ||||||
const blockquoteStyle = TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.blockquote.style; | ||||||
// Firefox border properties are serialized slightly differently | ||||||
const browserStyle = browserName === 'firefox' ? blockquoteStyle.replace(' border-left-style: solid', ' border-left: 6px solid gray') : blockquoteStyle; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw
Suggested change
|
||||||
|
||||||
await testMarkdownContentStyle({styleName: 'blockquote', style: browserStyle, page}); | ||||||
}); | ||||||
}); |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,139 @@ | ||||||||||
import {test, expect} from '@playwright/test'; | ||||||||||
import type {Locator, Page} from '@playwright/test'; | ||||||||||
import * as TEST_CONST from '../../testConstants'; | ||||||||||
import {checkCursorPosition, setupInput} from './utils'; | ||||||||||
|
||||||||||
const OPERATION_MODIFIER = process.platform === 'darwin' ? 'Meta' : 'Control'; | ||||||||||
|
||||||||||
const pasteContent = async ({text, page, inputLocator}: {text: string; page: Page; inputLocator: Locator}) => { | ||||||||||
await page.evaluate(async (pasteText) => navigator.clipboard.writeText(pasteText), text); | ||||||||||
await inputLocator.focus(); | ||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+v`); | ||||||||||
}; | ||||||||||
|
||||||||||
test.beforeEach(async ({page, context, browserName}) => { | ||||||||||
await page.goto(TEST_CONST.LOCAL_URL, {waitUntil: 'load'}); | ||||||||||
if (browserName === 'chromium') await context.grantPermissions(['clipboard-write', 'clipboard-read']); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use curly braces for conditionals everywhere. I believe we should have
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it wasn't enabled, i've added it |
||||||||||
}); | ||||||||||
|
||||||||||
test.describe('paste content', () => { | ||||||||||
test.skip(({browserName}) => !!process.env.CI && browserName === 'webkit', 'Excluded from webkit CI tests'); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
test('paste', async ({page}) => { | ||||||||||
const PASTE_TEXT = 'bold'; | ||||||||||
const boldStyleDefinition = TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.bold; | ||||||||||
|
||||||||||
const inputLocator = await setupInput(page, 'clear'); | ||||||||||
|
||||||||||
const wrappedText = boldStyleDefinition.wrapContent(PASTE_TEXT); | ||||||||||
await pasteContent({text: wrappedText, page, inputLocator}); | ||||||||||
|
||||||||||
const elementHandle = await inputLocator.locator('span', {hasText: PASTE_TEXT}).last(); | ||||||||||
let elementStyle; | ||||||||||
if (elementHandle) { | ||||||||||
await elementHandle.waitFor({state: 'attached'}); | ||||||||||
|
||||||||||
elementStyle = await elementHandle.getAttribute('style'); | ||||||||||
} | ||||||||||
expect(elementStyle).toEqual(boldStyleDefinition.style); | ||||||||||
}); | ||||||||||
|
||||||||||
test('paste replace', async ({page}) => { | ||||||||||
const inputLocator = await setupInput(page, 'reset'); | ||||||||||
|
||||||||||
await inputLocator.focus(); | ||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+a`); | ||||||||||
|
||||||||||
const newText = '*bold*'; | ||||||||||
await pasteContent({text: newText, page, inputLocator}); | ||||||||||
|
||||||||||
expect(await inputLocator.innerText()).toBe(newText); | ||||||||||
}); | ||||||||||
|
||||||||||
test('paste undo', async ({page, browserName}) => { | ||||||||||
test.skip(!!process.env.CI && browserName === 'firefox', 'Excluded from firefox CI tests'); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
const PASTE_TEXT_FIRST = '*bold*'; | ||||||||||
const PASTE_TEXT_SECOND = '@here'; | ||||||||||
|
||||||||||
const inputLocator = await setupInput(page, 'clear'); | ||||||||||
|
||||||||||
await page.evaluate(async (pasteText) => navigator.clipboard.writeText(pasteText), PASTE_TEXT_FIRST); | ||||||||||
|
||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+v`); | ||||||||||
await page.waitForTimeout(TEST_CONST.INPUT_HISTORY_DEBOUNCE_TIME_MS); | ||||||||||
await page.evaluate(async (pasteText) => navigator.clipboard.writeText(pasteText), PASTE_TEXT_SECOND); | ||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+v`); | ||||||||||
await page.waitForTimeout(TEST_CONST.INPUT_HISTORY_DEBOUNCE_TIME_MS); | ||||||||||
|
||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+z`); | ||||||||||
|
||||||||||
expect(await inputLocator.innerText()).toBe(PASTE_TEXT_FIRST); | ||||||||||
}); | ||||||||||
|
||||||||||
test('paste redo', async ({page}) => { | ||||||||||
const PASTE_TEXT_FIRST = '*bold*'; | ||||||||||
const PASTE_TEXT_SECOND = '@here'; | ||||||||||
|
||||||||||
const inputLocator = await setupInput(page, 'clear'); | ||||||||||
|
||||||||||
await page.evaluate(async (pasteText) => navigator.clipboard.writeText(pasteText), PASTE_TEXT_FIRST); | ||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+v`); | ||||||||||
await page.waitForTimeout(TEST_CONST.INPUT_HISTORY_DEBOUNCE_TIME_MS); | ||||||||||
await page.evaluate(async (pasteText) => navigator.clipboard.writeText(pasteText), PASTE_TEXT_SECOND); | ||||||||||
await page.waitForTimeout(TEST_CONST.INPUT_HISTORY_DEBOUNCE_TIME_MS); | ||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+v`); | ||||||||||
await page.waitForTimeout(TEST_CONST.INPUT_HISTORY_DEBOUNCE_TIME_MS); | ||||||||||
|
||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+z`); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using |
||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+Shift+z`); | ||||||||||
|
||||||||||
expect(await inputLocator.innerText()).toBe(`${PASTE_TEXT_FIRST}${PASTE_TEXT_SECOND}`); | ||||||||||
}); | ||||||||||
}); | ||||||||||
|
||||||||||
test('select', async ({page}) => { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
const inputLocator = await setupInput(page, 'reset'); | ||||||||||
await inputLocator.focus(); | ||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+a`); | ||||||||||
|
||||||||||
const cursorPosition = await page.evaluate(checkCursorPosition); | ||||||||||
|
||||||||||
expect(cursorPosition).toBe(TEST_CONST.EXAMPLE_CONTENT.length); | ||||||||||
}); | ||||||||||
|
||||||||||
test('cut content changes', async ({page, browserName}) => { | ||||||||||
test.skip(!!process.env.CI && browserName === 'webkit', 'Excluded from webkit CI tests'); | ||||||||||
|
||||||||||
const INITIAL_CONTENT = 'bold'; | ||||||||||
const WRAPPED_CONTENT = TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.bold.wrapContent(INITIAL_CONTENT); | ||||||||||
const EXPECTED_CONTENT = TEST_CONST.MARKDOWN_STYLE_DEFINITIONS.bold.wrapContent(INITIAL_CONTENT).slice(0, 3); | ||||||||||
|
||||||||||
const inputLocator = await setupInput(page, 'clear'); | ||||||||||
await pasteContent({text: WRAPPED_CONTENT, page, inputLocator}); | ||||||||||
const rootHandle = await inputLocator.locator('span.root').first(); | ||||||||||
|
||||||||||
await page.evaluate(async (initialContent) => { | ||||||||||
const filteredNode = Array.from(document.querySelectorAll('div[contenteditable="true"] > span.root span')).find((node) => { | ||||||||||
return node.textContent?.includes(initialContent) && node.nextElementSibling && node.nextElementSibling.textContent?.includes('*'); | ||||||||||
}); | ||||||||||
|
||||||||||
const startNode = filteredNode; | ||||||||||
const endNode = filteredNode?.nextElementSibling; | ||||||||||
|
||||||||||
if (startNode?.firstChild && endNode?.lastChild) { | ||||||||||
const range = new Range(); | ||||||||||
range.setStart(startNode.firstChild, 2); | ||||||||||
range.setEnd(endNode.lastChild, endNode.lastChild.textContent?.length ?? 0); | ||||||||||
|
||||||||||
const selection = window.getSelection(); | ||||||||||
selection?.removeAllRanges(); | ||||||||||
selection?.addRange(range); | ||||||||||
} | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting cursor position can also be moved to utils There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we probably won't be able to use it in here, because this would be only a part of the function used inside of page.evaluate and we aren't able to pass functions to the interior, as a param. I'll add this to the utils anyway, in case you're ever in need of using it somewhere else. |
||||||||||
}, INITIAL_CONTENT); | ||||||||||
|
||||||||||
await inputLocator.focus(); | ||||||||||
await inputLocator.press(`${OPERATION_MODIFIER}+x`); | ||||||||||
|
||||||||||
expect(await rootHandle.innerHTML()).toBe(EXPECTED_CONTENT); | ||||||||||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import type {Page} from '@playwright/test'; | ||
import * as TEST_CONST from '../../testConstants'; | ||
|
||
const setupInput = async (page: Page, action?: 'clear' | 'reset') => { | ||
const inputLocator = await page.locator(`div#${TEST_CONST.INPUT_ID}`); | ||
if (action) await page.click(`[data-testid="${action}"]`); | ||
|
||
return inputLocator; | ||
}; | ||
|
||
const checkCursorPosition = () => { | ||
const editableDiv = document.querySelector('div[contenteditable="true"]') as HTMLElement; | ||
const range = window.getSelection()?.getRangeAt(0); | ||
if (!range || !editableDiv) return null; | ||
const preCaretRange = range.cloneRange(); | ||
preCaretRange.selectNodeContents(editableDiv); | ||
preCaretRange.setEnd(range.endContainer, range.endOffset); | ||
return preCaretRange.toString().length; | ||
}; | ||
|
||
const setCursorPosition = ({startNode, endNode}: {startNode?: Element; endNode?: Element | null}) => { | ||
if (!startNode?.firstChild || !endNode?.lastChild) return null; | ||
|
||
const range = new Range(); | ||
range.setStart(startNode.firstChild, 2); | ||
range.setEnd(endNode.lastChild, endNode.lastChild.textContent?.length ?? 0); | ||
|
||
const selection = window.getSelection(); | ||
selection?.removeAllRanges(); | ||
selection?.addRange(range); | ||
|
||
return selection; | ||
}; | ||
|
||
export {setupInput, checkCursorPosition, setCursorPosition}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.