mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
834 lines
23 KiB
834 lines
23 KiB
import { expect, Locator } from '@playwright/test'; |
|
import { DocsOpenedPagePage } from '.'; |
|
import BasePage from '../../../Base'; |
|
|
|
export class TiptapPage extends BasePage { |
|
readonly openedPage: DocsOpenedPagePage; |
|
|
|
constructor(openedPage: DocsOpenedPagePage) { |
|
super(openedPage.rootPage); |
|
this.openedPage = openedPage; |
|
} |
|
|
|
get() { |
|
return this.openedPage.get().getByTestId('docs-page-content').locator('.ProseMirror'); |
|
} |
|
|
|
private async _click(locator: Locator) { |
|
await (await locator.elementHandle()).waitForElementState('stable'); |
|
|
|
const box = await (await locator.elementHandle()).boundingBox(); |
|
return this.rootPage.mouse.click(box.x + box.width / 2, box.y + box.height / 2); |
|
} |
|
|
|
private async _hover(locator: Locator) { |
|
// await (await locator.elementHandle()).waitForElementState('stable'); |
|
|
|
const box = await (await locator.elementHandle()).boundingBox(); |
|
await this.rootPage.mouse.move(box.x + box.width / 2, box.y + box.height / 2); |
|
} |
|
|
|
async selectNodes({ |
|
start, |
|
startType, |
|
end, |
|
endType, |
|
}: { |
|
start: number; |
|
startType?: string; |
|
end: number; |
|
endType?: string; |
|
}) { |
|
const startLocator = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${start + 1})`) |
|
.locator(`${startType ? tiptapNodeToDomType[startType] : 'p'}`); |
|
|
|
const endLocator = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${end + 1})`) |
|
.locator(`${endType ? tiptapNodeToDomType[endType] : 'p'}`); |
|
|
|
const startBox = await (await startLocator.elementHandle()).boundingBox(); |
|
const endBox = await (await endLocator.elementHandle()).boundingBox(); |
|
|
|
await this.rootPage.mouse.move(startBox.x, startBox.y); |
|
await this.rootPage.mouse.down(); |
|
await this.rootPage.waitForTimeout(400); |
|
await this.rootPage.mouse.move(endBox.x + endBox.width, endBox.y + endBox.height); |
|
} |
|
|
|
async openCommandMenu({ index }: { index?: number } = {}) { |
|
let paragraph; |
|
if (index !== undefined) { |
|
paragraph = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator('p:nth-child(1)'); |
|
} else { |
|
paragraph = this.get().locator('.draggable-block-wrapper:last-child').locator('p:nth-child(1)'); |
|
} |
|
await this._click(paragraph); |
|
|
|
await this.rootPage.waitForTimeout(400); |
|
|
|
await this.rootPage.keyboard.press('/'); |
|
|
|
await this.rootPage.locator('.nc-docs-command-list').waitFor({ state: 'visible' }); |
|
} |
|
|
|
async clickTextFormatButton(type: TextFormatType) { |
|
await this.rootPage.getByTestId(`nc-docs-editor-${type}-button`).click(); |
|
} |
|
|
|
async verifyTextFormatButtonActive({ type, active }: { type: TextFormatType; active: boolean }) { |
|
await expect(this.rootPage.getByTestId(`nc-docs-editor-${type}-button`)).toHaveAttribute( |
|
'aria-active', |
|
active.toString() |
|
); |
|
} |
|
|
|
async addNewNode({ |
|
type, |
|
index, |
|
link, |
|
filePath, |
|
noVerify, |
|
}: { |
|
type: TipTapNodes; |
|
index?: number; |
|
link?: string; |
|
filePath?: string; |
|
noVerify?: boolean; |
|
}) { |
|
await this.openCommandMenu({ index }); |
|
if (type === 'Image') { |
|
await this.attachFile({ |
|
filePath: [filePath], |
|
filePickUIAction: this.rootPage.getByTestId(`nc-docs-command-list-item-${type}`).click(), |
|
}); |
|
|
|
return; |
|
} |
|
await this.rootPage.getByTestId(`nc-docs-command-list-item-${type}`).click(); |
|
|
|
if (type === 'Embed iframe') { |
|
await this.rootPage.getByTestId('nc-docs-command-list-link-input').type(link); |
|
await this.rootPage.getByTestId('nc-docs-command-list-link-input').press('Enter'); |
|
} |
|
|
|
if (!noVerify) await this.rootPage.locator('.nc-docs-command-list').waitFor({ state: 'hidden' }); |
|
} |
|
|
|
async verifyErrorCommandMenu({ error }: { error: string }) { |
|
await expect(this.rootPage.getByTestId('nc-docs-command-list-link-input-error')).toHaveText(error); |
|
} |
|
|
|
async verifyHeaderNode({ index, type, content }: { index: number; type: TipTapNodes; content?: string }) { |
|
const level = type.split(' ')[1]; |
|
|
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.getByTestId(`nc-docs-tiptap-wrapper-${tiptapNodeLabels[type]}`) |
|
.locator(`h${level}`) |
|
).toBeVisible(); |
|
|
|
if (content) { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.getByTestId(`nc-docs-tiptap-wrapper-${tiptapNodeLabels[type]}`) |
|
.locator(`h${level}`) |
|
).toHaveText(content); |
|
} else { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.getByTestId(`nc-docs-tiptap-wrapper-${tiptapNodeLabels[type]}`) |
|
.locator(`h${level}`) |
|
).toHaveAttribute('data-placeholder', `Heading ${level}`); |
|
} |
|
} |
|
|
|
async clickBackButtonLinkCommandMenu() { |
|
await this._click(this.rootPage.getByTestId('nc-docs-command-list-link-back-btn')); |
|
} |
|
|
|
async verifyCommandMenuOpened({ isVisible }: { isVisible: boolean }) { |
|
if (isVisible) { |
|
await this.rootPage.locator('.nc-docs-command-list').waitFor({ state: 'visible' }); |
|
} else { |
|
await this.rootPage.locator('.nc-docs-command-list').waitFor({ state: 'hidden' }); |
|
} |
|
} |
|
|
|
async dragToNode({ |
|
fromIndex, |
|
toIndex, |
|
withoutHandle, |
|
}: { |
|
fromIndex: number; |
|
toIndex: number; |
|
withoutHandle?: boolean; |
|
}) { |
|
await this.openedPage.waitForRender(); |
|
|
|
const fromLocator = this.get().locator(`.draggable-block-wrapper:nth-child(${fromIndex + 1})`); |
|
const toLocator = this.get().locator(`.draggable-block-wrapper:nth-child(${toIndex + 1})`); |
|
|
|
await this._hover(fromLocator); |
|
let dragHandle = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${fromIndex + 1})`) |
|
.locator('div[data-drag-handle="true"][tiptap-draghandle="true"]'); |
|
if (withoutHandle) { |
|
dragHandle = fromLocator; |
|
} |
|
|
|
await dragHandle.dragTo(toLocator); |
|
await this.rootPage.waitForTimeout(200); |
|
} |
|
|
|
async fillContent({ |
|
content, |
|
index = 0, |
|
waitForNetwork = true, |
|
type = 'Paragraph', |
|
}: { |
|
content: string; |
|
index?: number; |
|
waitForNetwork?: boolean; |
|
type?: TipTapNodes; |
|
}) { |
|
await this.openedPage.waitForRender(); |
|
await this.rootPage.waitForTimeout(1000); |
|
|
|
let contentDomType; |
|
switch (type) { |
|
case 'Paragraph': |
|
contentDomType = 'p'; |
|
break; |
|
case 'Heading 1': |
|
contentDomType = 'h1'; |
|
break; |
|
case 'Heading 2': |
|
contentDomType = 'h2'; |
|
break; |
|
case 'Heading 3': |
|
contentDomType = 'h3'; |
|
break; |
|
default: |
|
contentDomType = 'p'; |
|
break; |
|
} |
|
|
|
const waitNetwork = waitForNetwork |
|
? this.rootPage.waitForResponse(async response => { |
|
return response.url().includes('api/v1/docs/page') && response.request().method() === 'PUT'; |
|
}) |
|
: Promise.resolve(); |
|
|
|
await this.get().click({ |
|
force: true, |
|
}); |
|
|
|
await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`${type ? tiptapNodeToDomType[type] : 'p'}`) |
|
.click({ |
|
force: true, |
|
}); |
|
|
|
for (const char of content) { |
|
await this.rootPage.keyboard.type(char); |
|
} |
|
|
|
await waitNetwork; |
|
} |
|
|
|
async scrollToNode({ index }: { index: number }) { |
|
await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.scrollIntoViewIfNeeded(); |
|
} |
|
|
|
async clickLastNode({ start }) { |
|
await this.get() |
|
.locator(`.draggable-block-wrapper:last-child`) |
|
.locator('.node-view-drag-content') |
|
.click({ |
|
force: true, |
|
position: start |
|
? { |
|
x: 0, |
|
y: 0, |
|
} |
|
: undefined, |
|
}); |
|
} |
|
|
|
async clickNode({ index, start }: { index: number; start: boolean }) { |
|
await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator('.node-view-drag-content') |
|
.click({ |
|
force: true, |
|
position: start |
|
? { |
|
x: 0, |
|
y: 0, |
|
} |
|
: undefined, |
|
}); |
|
} |
|
|
|
async toggleTaskNode({ index }: { index: number }) { |
|
await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator('input[type="checkbox"]') |
|
.click({ |
|
force: true, |
|
}); |
|
} |
|
|
|
private async verifyHistory({ |
|
node, |
|
history, |
|
nodeType, |
|
content, |
|
}: { |
|
node: Locator; |
|
nodeType: TipTapNodes; |
|
content: string; |
|
history: { |
|
added?: boolean; |
|
removed?: boolean; |
|
}; |
|
}) { |
|
const diffType = history.added ? 'ins' : 'del'; |
|
|
|
let found = false; |
|
// In case of text diff, section node diff attribute is not set |
|
for (let i = 0; i < 3; i++) { |
|
if ((await node.getAttribute('data-diff-node')) === diffType) { |
|
found = true; |
|
break; |
|
} |
|
await this.rootPage.waitForTimeout(200); |
|
} |
|
|
|
// As text diff is shown in a different way, we need to check if it's visible |
|
if (!found) { |
|
const textDiffHtmlTag = diffType; |
|
if (nodeType === 'Paragraph') { |
|
await expect(node.locator(textDiffHtmlTag)).toBeVisible(); |
|
await expect(node.locator(textDiffHtmlTag)).toHaveText(content); |
|
} else { |
|
await expect(node.locator(`[data-diff-node="${textDiffHtmlTag}"]`)).toBeVisible(); |
|
} |
|
return; |
|
} |
|
|
|
await expect(node).toHaveAttribute('data-diff-node', diffType); |
|
} |
|
|
|
async verifyNode({ |
|
index, |
|
type, |
|
content, |
|
childParagraphCount, |
|
childParagraph, |
|
isUploading, |
|
placeholder, |
|
history, |
|
}: { |
|
index: number; |
|
type?: TipTapNodes; |
|
content?: string; |
|
childParagraphCount?: number; |
|
childParagraph?: { index: number; content: string }; |
|
isUploading?: boolean; |
|
placeholder?: string; |
|
history?: { |
|
added?: boolean; |
|
removed?: boolean; |
|
} | null; |
|
}) { |
|
type = type || 'Paragraph'; |
|
const node = this.get().locator(`.draggable-block-wrapper:nth-child(${index + 1})`); |
|
|
|
if (history) { |
|
await this.verifyHistory({ node, history, nodeType: type, content }); |
|
} |
|
|
|
if (type === 'Embed iframe') { |
|
await expect(node.locator('.external-content-wrapper').locator('iframe')).toHaveAttribute('src', content); |
|
return; |
|
} |
|
|
|
if (isUploading) { |
|
await expect( |
|
node.getByTestId(`nc-docs-tiptap-wrapper-${tiptapNodeLabels[type]}`).locator(tiptapNodeToDomType[type]) |
|
).toHaveAttribute('isuploading', 'true'); |
|
await expect( |
|
node.getByTestId(`nc-docs-tiptap-wrapper-${tiptapNodeLabels[type]}`).locator('.image-uploading-wrapper') |
|
).toContainText('Uploading...'); |
|
} |
|
|
|
await expect( |
|
node.getByTestId(`nc-docs-tiptap-wrapper-${tiptapNodeLabels[type]}`).locator(tiptapNodeToDomType[type]) |
|
).toBeVisible(); |
|
|
|
if (content) { |
|
await expect(node).toContainText(content); |
|
} |
|
|
|
if (type) { |
|
await expect(node.locator('.node-view-drag-content')).toHaveAttribute( |
|
'data-testid', |
|
`nc-docs-tiptap-wrapper-${tiptapNodeLabels[type]}` |
|
); |
|
} |
|
|
|
if (childParagraphCount) { |
|
await expect(node.locator('.node-view-drag-content').locator('p')).toHaveCount(childParagraphCount); |
|
} |
|
|
|
if (childParagraph) { |
|
await expect( |
|
node.locator('.node-view-drag-content').locator(`p:nth-child(${childParagraph.index + 1})`) |
|
).toHaveText(childParagraph.content); |
|
} |
|
|
|
if (placeholder) { |
|
await expect( |
|
node.getByTestId(`nc-docs-tiptap-wrapper-${tiptapNodeLabels[type]}`).locator(tiptapNodeToDomType[type]) |
|
).toHaveAttribute('data-placeholder', placeholder); |
|
} |
|
} |
|
|
|
async verifyContent({ content }: { content: string }) { |
|
await expect(this.get()).toHaveText(content); |
|
} |
|
|
|
async verifyNodeSelected({ index }: { index: number }) { |
|
await expect(this.get().locator(`.draggable-block-wrapper:nth-child(${index + 1})`)).toHaveClass(/focused/); |
|
} |
|
|
|
async verifyListNode({ |
|
index, |
|
level, |
|
type, |
|
content, |
|
nestedIndex, |
|
checked, |
|
}: { |
|
index: number; |
|
level: number; |
|
type: TipTapNodes; |
|
content: string; |
|
nestedIndex?: number; |
|
checked?: boolean; |
|
}) { |
|
await this.verifyNode({ index, type, content }); |
|
|
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`[data-type="${tiptapNodeLabels[type]}"]`) |
|
).toHaveCSS('padding-left', `${level * 16}px`); |
|
|
|
if (nestedIndex && type === 'Numbered List') { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`.tiptap-list-item-start > span`) |
|
).toHaveAttribute('data-number', `${nestedIndex + 1}`); |
|
} |
|
|
|
if (checked !== undefined && type === 'Task List') { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`input[type="checkbox"]`) |
|
).toBeChecked({ checked }); |
|
} |
|
} |
|
|
|
async fillTableCell({ |
|
index, |
|
row, |
|
column, |
|
content, |
|
}: { |
|
index: number; |
|
row: number; |
|
column: number; |
|
content: string; |
|
}) { |
|
await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(${row + 1})`) |
|
.locator(`td:nth-child(${column + 1})`) |
|
.click({ |
|
force: true, |
|
}); |
|
|
|
await this.rootPage.keyboard.type(content); |
|
} |
|
|
|
async addTableRow({ index, rowIndex, kind }: { index: number; rowIndex?: number; kind: 'above' | 'below' | 'end' }) { |
|
if (kind === 'end') { |
|
const addNewRowButton = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.tiptap-create-row-btn`); |
|
|
|
await addNewRowButton.hover(); |
|
await this._click(addNewRowButton); |
|
|
|
return; |
|
} |
|
|
|
const rowLocator = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(${rowIndex + 1})`); |
|
await this._hover(rowLocator); |
|
|
|
const rowDragHandle = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(${rowIndex + 1})`) |
|
.getByTestId('nc-docs-table-row-drag-handle-wrapper'); |
|
await this._click(rowDragHandle); |
|
|
|
const insertRowLocator = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(${rowIndex + 1})`) |
|
.getByTestId(kind === 'above' ? 'nc-docs-table-row-insert-above' : 'nc-docs-table-row-insert-below'); |
|
await this._click(insertRowLocator); |
|
} |
|
|
|
async addTableColumn({ |
|
index, |
|
columnIndex, |
|
kind, |
|
}: { |
|
index: number; |
|
columnIndex?: number; |
|
kind: 'left' | 'right' | 'end'; |
|
}) { |
|
if (kind === 'end') { |
|
const addNewColumnButton = this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.tiptap-create-column-btn`); |
|
|
|
await addNewColumnButton.hover(); |
|
await addNewColumnButton.click(); |
|
|
|
return; |
|
} |
|
|
|
await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(1)`) |
|
.locator(`td:nth-child(${columnIndex + 1})`) |
|
.hover(); |
|
|
|
await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(1)`) |
|
.locator(`td:nth-child(${columnIndex + 1})`) |
|
.getByTestId('nc-docs-table-column-drag-handle-wrapper') |
|
.click({ |
|
force: true, |
|
}); |
|
await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(1)`) |
|
.locator(`td:nth-child(${columnIndex + 1})`) |
|
.getByTestId(kind === 'left' ? 'nc-docs-table-column-insert-left' : 'nc-docs-table-column-insert-right') |
|
.click({ |
|
force: true, |
|
}); |
|
} |
|
|
|
async deleteTableRow({ index, rowIndex }: { index: number; rowIndex?: number }) { |
|
await this._hover( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(${rowIndex + 1})`) |
|
); |
|
|
|
await this._click( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(${rowIndex + 1})`) |
|
.getByTestId('nc-docs-table-row-drag-handle-wrapper') |
|
); |
|
|
|
await this._click( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(${rowIndex + 1})`) |
|
.getByTestId('nc-docs-table-row-delete') |
|
); |
|
} |
|
|
|
async deleteTableColumn({ index, columnIndex }: { index: number; columnIndex?: number }) { |
|
await this._hover( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(1)`) |
|
.locator(`td:nth-child(${columnIndex + 1})`) |
|
); |
|
|
|
await this._click( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(1)`) |
|
.locator(`td:nth-child(${columnIndex + 1})`) |
|
.getByTestId('nc-docs-table-column-drag-handle-wrapper') |
|
); |
|
|
|
await this._click( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(1)`) |
|
.locator(`td:nth-child(${columnIndex + 1})`) |
|
.getByTestId('nc-docs-table-column-delete') |
|
); |
|
} |
|
|
|
async verifyTextFormatting({ |
|
index, |
|
text, |
|
formatType, |
|
}: { |
|
index: number; |
|
text: string; |
|
formatType: 'bold' | 'italic' | 'strike' | 'underline' | 'code'; |
|
}) { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`${tiptapTextFormatToDomType[formatType]}`) |
|
).toHaveText(text); |
|
} |
|
|
|
async verifyTableNode({ |
|
index, |
|
cells, |
|
rowCount, |
|
columnCount, |
|
}: { |
|
index: number; |
|
cells: { |
|
row: number; |
|
column: number; |
|
content: string; |
|
}[]; |
|
rowCount?: number; |
|
columnCount?: number; |
|
}) { |
|
await this.verifyNode({ index, type: 'Table' }); |
|
|
|
for (const cell of cells) { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(${cell.row + 1})`) |
|
.locator(`td:nth-child(${cell.column + 1})`) |
|
).toHaveText(cell.content); |
|
} |
|
|
|
if (rowCount) { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr`) |
|
).toHaveCount(rowCount); |
|
} |
|
|
|
if (columnCount) { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`tr:nth-child(1)`) |
|
.locator(`td`) |
|
).toHaveCount(columnCount); |
|
} |
|
} |
|
|
|
async verifyLinkNode({ index, placeholder, url }: { index: number; placeholder: string; url?: string }) { |
|
if (url) { |
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`a`) |
|
).toHaveAttribute('href', url); |
|
} |
|
|
|
await expect( |
|
this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`a`) |
|
).toHaveText(placeholder); |
|
} |
|
|
|
async verifyLinkOptionVisible({ visible }: { visible: boolean }) { |
|
if (visible) { |
|
await expect(this.rootPage.getByTestId('nc-docs-link-options')).toBeVisible(); |
|
} else { |
|
await expect(this.rootPage.getByTestId('nc-docs-link-options')).toBeHidden(); |
|
} |
|
} |
|
|
|
async verifyLinkOptionSearchResults({ titles, selectedTitle }: { titles?: string[]; selectedTitle?: string }) { |
|
if (titles) { |
|
for (const title of titles) { |
|
await expect(this.rootPage.getByTestId(`nc-docs-link-option-searched-page-${title}`)).toContainText(title); |
|
} |
|
|
|
const count = titles.length; |
|
await expect(this.rootPage.getByTestId('nc-docs-link-option-searched-pages').locator('.page-item')).toHaveCount( |
|
count |
|
); |
|
} |
|
|
|
if (selectedTitle) { |
|
await expect(this.rootPage.getByTestId(`nc-docs-link-option-searched-page-${selectedTitle}`)).toHaveClass( |
|
/selected/ |
|
); |
|
} |
|
} |
|
|
|
async gotoStoredLink({ index }: { index: number }) { |
|
const linkHref = await this.get() |
|
.locator(`.draggable-block-wrapper:nth-child(${index + 1})`) |
|
.locator(`.node-view-drag-content`) |
|
.locator(`a`) |
|
.getAttribute('href'); |
|
|
|
await this.rootPage.goto(linkHref); |
|
await this.openedPage.waitForRender(); |
|
} |
|
|
|
async clickLinkDeleteButton() { |
|
await this.rootPage.getByTestId('nc-docs-link-options-open-delete').click(); |
|
} |
|
|
|
async clearContent() { |
|
await this.openedPage.waitForRender(); |
|
|
|
await this.get().click(); |
|
|
|
const firstParagraph = this.get() |
|
.locator('.draggable-block-wrapper:nth-child(1)') |
|
.locator('.node-view-drag-content'); |
|
await firstParagraph.click(); |
|
await this.rootPage.waitForTimeout(500); |
|
await this.rootPage.keyboard.press('Meta+A'); |
|
await this.rootPage.keyboard.press('Backspace'); |
|
|
|
// TODO: fix this |
|
await this.rootPage.waitForTimeout(500); |
|
|
|
// await this.waitForResponse({ |
|
// uiAction: () => firstParagraph.clear(), |
|
// httpMethodsToMatch: ['PUT'], |
|
// requestUrlPathToMatch: `api/v1/docs/page`, |
|
// }); |
|
} |
|
} |
|
|
|
export type TipTapNodes = |
|
| 'Heading 1' |
|
| 'Heading 2' |
|
| 'Heading 3' |
|
| 'Paragraph' |
|
| 'Quote' |
|
| 'Code' |
|
| 'Bullet List' |
|
| 'Numbered List' |
|
| 'Task List' |
|
| 'Divider' |
|
| 'Image' |
|
| 'Table' |
|
| 'Link' |
|
| 'Emoji' |
|
| 'Info notice' |
|
| 'Warning notice' |
|
| 'Tip notice' |
|
| 'Embed iframe'; |
|
|
|
export type TextFormatType = |
|
| 'bold' |
|
| 'italic' |
|
| 'underline' |
|
| 'strike' |
|
| 'task' |
|
| 'bullet' |
|
| 'ordered' |
|
| 'link' |
|
| 'expand'; |
|
|
|
const tiptapNodeLabels: Record<TipTapNodes, string> = { |
|
'Info notice': 'infoCallout', |
|
'Warning notice': 'warningCallout', |
|
'Tip notice': 'tipCallout', |
|
Code: 'codeBlock', |
|
Paragraph: 'paragraph', |
|
'Heading 1': 'heading', |
|
'Heading 2': 'heading', |
|
'Heading 3': 'heading', |
|
Divider: 'divider', |
|
Image: 'image', |
|
'Bullet List': 'bullet', |
|
'Numbered List': 'ordered', |
|
'Task List': 'task', |
|
Table: 'table', |
|
Quote: 'quote', |
|
}; |
|
|
|
const tiptapNodeToDomType: Record<TipTapNodes, string> = { |
|
'Heading 1': 'h1', |
|
'Heading 2': 'h2', |
|
'Heading 3': 'h3', |
|
'Info notice': 'div.info-callout', |
|
'Warning notice': 'div.warning-callout', |
|
'Tip notice': 'div.tip-callout', |
|
Paragraph: 'p', |
|
Divider: 'hr', |
|
'Embed iframe': 'iframe', |
|
Image: 'img', |
|
'Bullet List': 'div[data-type="bullet"]', |
|
'Numbered List': 'div[data-type="ordered"]', |
|
'Task List': 'div[data-type="task"]', |
|
Table: 'div.tiptap-table-wrapper', |
|
Code: 'pre', |
|
Quote: 'quote', |
|
}; |
|
|
|
const tiptapTextFormatToDomType: Record<'bold' | 'italic' | 'underline' | 'strike' | 'code', string> = { |
|
bold: 'strong', |
|
italic: 'em', |
|
underline: 'u', |
|
strike: 's', |
|
code: 'code', |
|
};
|
|
|