fix: write tests for tooltip.ts and fix lint issues

This commit is contained in:
JackUait 2025-11-11 04:17:03 +03:00
commit f02e838dc7
45 changed files with 2109 additions and 459 deletions

View file

@ -10,8 +10,44 @@ export const selectionChangeDebounceTimeout = 180;
*/
export const modificationsObserverBatchTimeout = 400;
/**
* The data-interface attribute name
* Used as a single source of truth for the data-interface attribute
*/
export const DATA_INTERFACE_ATTRIBUTE = 'data-interface';
/**
* Value for the data-interface attribute on editor wrapper elements
* Used as a single source of truth for editor identification
*/
export const EDITOR_INTERFACE_VALUE = 'editorjs';
/**
* Value for the data-interface attribute on inline toolbar elements
* Used as a single source of truth for inline toolbar identification
*/
export const INLINE_TOOLBAR_INTERFACE_VALUE = 'inline-toolbar';
/**
* Value for the data-interface attribute on tooltip elements
* Used as a single source of truth for tooltip identification
*/
export const TOOLTIP_INTERFACE_VALUE = 'tooltip';
/**
* CSS selector for the main editor wrapper element
* Used to identify the editor container in the DOM
*/
export const EDITOR_INTERFACE_SELECTOR = '[data-interface=editorjs]';
/**
* CSS selector for tooltip elements
* Used to identify tooltip elements in the DOM
*/
export const TOOLTIP_INTERFACE_SELECTOR = '[data-interface="tooltip"]';
/**
* CSS selector for inline toolbar elements
* Used to identify inline toolbar elements in the DOM
*/
export const INLINE_TOOLBAR_INTERFACE_SELECTOR = '[data-interface=inline-toolbar]';

View file

@ -1,5 +1,5 @@
import type { Tooltip as ITooltip } from '../../../../types/api';
import type { TooltipOptions, TooltipContent } from 'codex-tooltip/types';
import type { TooltipOptions, TooltipContent } from '../../utils/tooltip';
import Module from '../../__module';
import type { ModuleConfig } from '../../../types-internal/module-config';
import * as tooltip from '../../utils/tooltip';

View file

@ -13,6 +13,7 @@ import type { Popover, PopoverItemHtmlParams, PopoverItemParams, WithChildren }
import { PopoverItemType } from '../../utils/popover';
import { PopoverInline } from '../../utils/popover/popover-inline';
import type InlineToolAdapter from 'src/components/tools/inline';
import { DATA_INTERFACE_ATTRIBUTE, INLINE_TOOLBAR_INTERFACE_VALUE } from '../../constants';
/**
* Inline Toolbar elements
@ -159,7 +160,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
...(this.isRtl ? [ this.Editor.UI.CSS.editorRtlFix ] : []),
]);
this.nodes.wrapper.setAttribute('data-interface', 'inline-toolbar');
this.nodes.wrapper.setAttribute(DATA_INTERFACE_ATTRIBUTE, INLINE_TOOLBAR_INTERFACE_VALUE);
this.nodes.wrapper.setAttribute('data-cy', 'inline-toolbar');
/**

View file

@ -15,7 +15,7 @@ import { mobileScreenBreakpoint } from '../utils';
import styles from '../../styles/main.css?inline';
import { BlockHovered } from '../events/BlockHovered';
import { selectionChangeDebounceTimeout } from '../constants';
import { DATA_INTERFACE_ATTRIBUTE, EDITOR_INTERFACE_VALUE, selectionChangeDebounceTimeout } from '../constants';
import { EditorMobileLayoutToggled } from '../events';
/**
* HTML Elements used for UI
@ -288,7 +288,7 @@ export default class UI extends Module<UINodes> {
this.CSS.editorWrapper,
...(this.isRtl ? [ this.CSS.editorRtlFix ] : []),
]);
this.nodes.wrapper.setAttribute('data-interface', 'editorjs');
this.nodes.wrapper.setAttribute(DATA_INTERFACE_ATTRIBUTE, EDITOR_INTERFACE_VALUE);
this.nodes.redactor = $.make('div', this.CSS.editorZone);
/**

View file

@ -1,71 +1,616 @@
/* eslint-disable jsdoc/no-undefined-types */
/**
* Use external module CodeX Tooltip
*/
import CodeXTooltips from 'codex-tooltip';
import type { TooltipOptions, TooltipContent } from 'codex-tooltip/types';
import tooltipStyles from '../../styles/tooltip.css?inline';
import { DATA_INTERFACE_ATTRIBUTE, TOOLTIP_INTERFACE_VALUE } from '../constants';
/**
* Tooltips lib: CodeX Tooltips
*
* @see https://github.com/codex-team/codex.tooltips
* Tooltip supported content
*/
let lib: null | CodeXTooltips = null;
export type TooltipContent = HTMLElement | DocumentFragment | Node | string;
/**
* If library is needed, but it is not initialized yet, this function will initialize it
*
* For example, if editor was destroyed and then initialized again
* Base options interface for tooltips
*/
function prepare(): void {
if (lib) {
return;
export interface TooltipOptions {
/**
* Tooltip placement: top|bottom|left|right
*/
placement?: string;
/**
* Tooltip top margin
*/
marginTop?: number;
/**
* Tooltip left margin
*/
marginLeft?: number;
/**
* Tooltip right margin
*/
marginRight?: number;
/**
* Tooltip bottom margin
*/
marginBottom?: number;
/**
* Timout before showing
*/
delay?: number;
/**
* Timout before hiding
*/
hidingDelay?: number;
}
const DEFAULT_OFFSET = 10;
const TOOLTIP_ROLE = 'tooltip';
const ARIA_HIDDEN_ATTRIBUTE = 'aria-hidden';
const ARIA_HIDDEN_FALSE = 'false';
const ARIA_HIDDEN_TRUE = 'true';
const VISIBILITY_PROPERTY = 'visibility';
const VISIBILITY_VISIBLE = 'visible';
const VISIBILITY_HIDDEN = 'hidden';
type CSSTooltipClasses = {
tooltip: string;
tooltipContent: string;
tooltipShown: string;
placement: {
left: string;
bottom: string;
right: string;
top: string;
};
};
/**
* Tiny any beautiful tooltips module.
*
* Can be showed near passed Element with any specified HTML content
*
* Based on codex-tooltip: https://github.com/codex-team/codex.tooltips
*
* @author CodeX <codex.so>
* @license MIT
*/
class Tooltip {
/**
* Tooltip CSS classes
*
* @returns {object} CSS class names
*/
private get CSS(): CSSTooltipClasses {
return {
tooltip: 'ct',
tooltipContent: 'ct__content',
tooltipShown: 'ct--shown',
placement: {
left: 'ct--left',
bottom: 'ct--bottom',
right: 'ct--right',
top: 'ct--top',
},
};
}
lib = new CodeXTooltips();
/**
* Module nodes
*/
private nodes: {
wrapper: HTMLElement | null;
content: HTMLElement | null;
} = {
wrapper: null,
content: null,
};
/**
* Appearance state
*/
private showed: boolean = false;
/**
* Offset above the Tooltip
*/
private offsetTop: number = DEFAULT_OFFSET;
/**
* Offset at the left from the Tooltip
*/
private offsetLeft: number = DEFAULT_OFFSET;
/**
* Offset at the right from the Tooltip
*/
private offsetRight: number = DEFAULT_OFFSET;
/**
* Store timeout before showing to clear it on hide
*/
private showingTimeout: ReturnType<typeof setTimeout> | null = null;
/**
* How many milliseconds need to wait before hiding
*/
private hidingDelay: number = 0;
/**
* Store timeout before hiding
*/
private hidingTimeout: ReturnType<typeof setTimeout> | null = null;
/**
* MutationObserver for watching tooltip visibility changes
*/
private ariaObserver: MutationObserver | null = null;
/**
* Static singleton instance
*/
private static instance: Tooltip | null = null;
/**
* Get singleton instance
*/
public static getInstance(): Tooltip {
if (!Tooltip.instance) {
Tooltip.instance = new Tooltip();
}
return Tooltip.instance;
}
/**
* Module constructor
*/
private constructor() {
this.loadStyles();
this.prepare();
window.addEventListener('scroll', this.handleWindowScroll, { passive: true });
}
/**
* Show Tooltip near passed element with specified HTML content
*
* @param {HTMLElement} element - target element to place Tooltip near that
* @param {TooltipContent} content any HTML Element of String that will be used as content
* @param {TooltipOptions} options Available options {@link TooltipOptions}
*/
public show(element: HTMLElement, content: TooltipContent, options: TooltipOptions = {}): void {
if (!this.nodes.wrapper) {
this.prepare();
}
if (this.hidingTimeout) {
clearTimeout(this.hidingTimeout);
this.hidingTimeout = null;
}
const basicOptions = {
placement: 'bottom',
marginTop: 0,
marginLeft: 0,
marginRight: 0,
marginBottom: 0,
delay: 70,
hidingDelay: 0,
};
const showingOptions = Object.assign(basicOptions, options);
if (showingOptions.hidingDelay) {
this.hidingDelay = showingOptions.hidingDelay;
}
if (!this.nodes.content) {
return;
}
this.nodes.content.innerHTML = '';
if (typeof content === 'string') {
this.nodes.content.appendChild(document.createTextNode(content));
} else if (content instanceof Node) {
this.nodes.content.appendChild(content);
} else {
throw Error('[CodeX Tooltip] Wrong type of «content» passed. It should be an instance of Node or String. ' +
'But ' + typeof content + ' given.');
}
if (!this.nodes.wrapper) {
return;
}
this.nodes.wrapper.classList.remove(...Object.values(this.CSS.placement));
switch (showingOptions.placement) {
case 'top':
this.placeTop(element, showingOptions);
break;
case 'left':
this.placeLeft(element, showingOptions);
break;
case 'right':
this.placeRight(element, showingOptions);
break;
case 'bottom':
default:
this.placeBottom(element, showingOptions);
break;
}
if (showingOptions && showingOptions.delay) {
this.showingTimeout = setTimeout(() => {
if (this.nodes.wrapper) {
this.nodes.wrapper.classList.add(this.CSS.tooltipShown);
this.updateTooltipVisibility();
}
this.showed = true;
}, showingOptions.delay);
} else {
if (this.nodes.wrapper) {
this.nodes.wrapper.classList.add(this.CSS.tooltipShown);
this.updateTooltipVisibility();
}
this.showed = true;
}
}
/**
* Hide toolbox tooltip and clean content
*
* @param {boolean} skipDelay - forces hiding immediately
*/
public hide(skipDelay: boolean = false): void {
if (this.hidingDelay && !skipDelay) {
// debounce
if (this.hidingTimeout) {
clearTimeout(this.hidingTimeout);
}
this.hidingTimeout = setTimeout(() => {
this.hide(true);
}, this.hidingDelay);
return;
}
if (this.nodes.wrapper) {
this.nodes.wrapper.classList.remove(this.CSS.tooltipShown);
this.updateTooltipVisibility();
}
this.showed = false;
if (this.showingTimeout) {
clearTimeout(this.showingTimeout);
this.showingTimeout = null;
}
}
/**
* Mouseover/Mouseleave decorator
*
* @param {HTMLElement} element - target element to place Tooltip near that
* @param {TooltipContent} content any HTML Element of String that will be used as content
* @param {TooltipOptions} options Available options {@link TooltipOptions}
*/
public onHover(element: HTMLElement, content: TooltipContent, options: TooltipOptions = {}): void {
element.addEventListener('mouseenter', () => {
this.show(element, content, options);
});
element.addEventListener('mouseleave', () => {
this.hide();
});
}
/**
* Release DOM and event listeners
*/
public destroy(): void {
this.ariaObserver?.disconnect();
this.ariaObserver = null;
if (this.nodes.wrapper) {
this.nodes.wrapper.remove();
}
window.removeEventListener('scroll', this.handleWindowScroll);
Tooltip.instance = null;
}
/**
* Hide tooltip when page is scrolled
*/
private handleWindowScroll = (): void => {
if (this.showed) {
this.hide(true);
}
};
/**
* Module Preparation method
*/
private prepare(): void {
this.nodes.wrapper = this.make('div', this.CSS.tooltip);
this.nodes.wrapper.setAttribute(DATA_INTERFACE_ATTRIBUTE, TOOLTIP_INTERFACE_VALUE);
this.nodes.content = this.make('div', this.CSS.tooltipContent);
if (this.nodes.wrapper && this.nodes.content) {
this.append(this.nodes.wrapper, this.nodes.content);
this.append(document.body, this.nodes.wrapper);
this.ensureTooltipAttributes();
}
}
/**
* Update tooltip visibility based on shown state
*/
private updateTooltipVisibility(): void {
if (!this.nodes.wrapper) {
return;
}
const isShown = this.nodes.wrapper.classList.contains(this.CSS.tooltipShown);
this.nodes.wrapper.style.setProperty(VISIBILITY_PROPERTY, isShown ? VISIBILITY_VISIBLE : VISIBILITY_HIDDEN);
this.nodes.wrapper.setAttribute(ARIA_HIDDEN_ATTRIBUTE, isShown ? ARIA_HIDDEN_FALSE : ARIA_HIDDEN_TRUE);
}
/**
* Watch tooltip visibility changes for accessibility
*/
private watchTooltipVisibility(): void {
if (!this.nodes.wrapper) {
return;
}
this.ariaObserver?.disconnect();
this.updateTooltipVisibility();
this.ariaObserver = new MutationObserver(() => {
this.updateTooltipVisibility();
});
this.ariaObserver.observe(this.nodes.wrapper, {
attributes: true,
attributeFilter: [ 'class' ],
});
}
/**
* Ensure tooltip has proper accessibility attributes
*/
private ensureTooltipAttributes(): void {
if (!this.nodes.wrapper) {
return;
}
if (!this.nodes.wrapper.hasAttribute(DATA_INTERFACE_ATTRIBUTE) || this.nodes.wrapper.getAttribute(DATA_INTERFACE_ATTRIBUTE) !== TOOLTIP_INTERFACE_VALUE) {
this.nodes.wrapper.setAttribute(DATA_INTERFACE_ATTRIBUTE, TOOLTIP_INTERFACE_VALUE);
}
this.nodes.wrapper.setAttribute('role', TOOLTIP_ROLE);
this.watchTooltipVisibility();
}
/**
* Append CSS file
*/
private loadStyles(): void {
const id = 'codex-tooltips-style';
if (document.getElementById(id)) {
return;
}
const tag = this.make('style', null, {
textContent: tooltipStyles,
id,
});
/**
* Append styles at the top of HEAD tag
*/
this.prepend(document.head, tag);
}
/**
* Calculates element coords and moves tooltip bottom of the element
*
* @param {HTMLElement} element - target element
* @param {TooltipOptions} showingOptions - placement options
*/
private placeBottom(element: HTMLElement, showingOptions: TooltipOptions): void {
if (!this.nodes.wrapper) {
return;
}
const elementCoords = element.getBoundingClientRect();
const left = elementCoords.left + element.clientWidth / 2 - this.nodes.wrapper.offsetWidth / 2;
const top = elementCoords.bottom + window.pageYOffset + this.offsetTop + (showingOptions.marginTop ?? 0);
this.applyPlacement('bottom', left, top);
}
/**
* Calculates element coords and moves tooltip top of the element
*
* @param {HTMLElement} element - target element
* @param {TooltipOptions} _showingOptions - placement options (unused for top placement)
*/
private placeTop(element: HTMLElement, _showingOptions: TooltipOptions): void {
if (!this.nodes.wrapper) {
return;
}
const elementCoords = element.getBoundingClientRect();
const left = elementCoords.left + element.clientWidth / 2 - this.nodes.wrapper.offsetWidth / 2;
const top = elementCoords.top + window.pageYOffset - this.nodes.wrapper.clientHeight - this.offsetTop;
this.applyPlacement('top', left, top);
}
/**
* Calculates element coords and moves tooltip left of the element
*
* @param {HTMLElement} element - target element
* @param {TooltipOptions} showingOptions - placement options
*/
private placeLeft(element: HTMLElement, showingOptions: TooltipOptions): void {
if (!this.nodes.wrapper) {
return;
}
const elementCoords = element.getBoundingClientRect();
const left = elementCoords.left - this.nodes.wrapper.offsetWidth - this.offsetLeft - (showingOptions.marginLeft ?? 0);
const top = elementCoords.top + window.pageYOffset + element.clientHeight / 2 - this.nodes.wrapper.offsetHeight / 2;
this.applyPlacement('left', left, top);
}
/**
* Calculates element coords and moves tooltip right of the element
*
* @param {HTMLElement} element - target element
* @param {TooltipOptions} showingOptions - placement options
*/
private placeRight(element: HTMLElement, showingOptions: TooltipOptions): void {
if (!this.nodes.wrapper) {
return;
}
const elementCoords = element.getBoundingClientRect();
const left = elementCoords.right + this.offsetRight + (showingOptions.marginRight ?? 0);
const top = elementCoords.top + window.pageYOffset + element.clientHeight / 2 - this.nodes.wrapper.offsetHeight / 2;
this.applyPlacement('right', left, top);
}
/**
* Set wrapper position
*
* @param {string} place - placement direction
* @param {number} left - left position in pixels
* @param {number} top - top position in pixels
*/
private applyPlacement(place: 'top' | 'bottom' | 'left' | 'right', left: number, top: number): void {
if (!this.nodes.wrapper) {
return;
}
this.nodes.wrapper.classList.add(this.CSS.placement[place]);
this.nodes.wrapper.style.left = `${left}px`;
this.nodes.wrapper.style.top = `${top}px`;
}
/**
* Helper for making Elements with classname and attributes
*
* @param {string} tagName - new Element tag name
* @param {Array<string>|string} classNames - list or name of CSS classname(s)
* @param {object} attributes - any attributes
* @returns {HTMLElement}
*/
private make(tagName: string, classNames: string | string[] | null = null, attributes: Record<string, unknown> = {}): HTMLElement {
const el = document.createElement(tagName);
if (Array.isArray(classNames)) {
el.classList.add(...classNames);
} else if (classNames) {
el.classList.add(classNames);
}
for (const attrName in attributes) {
if (Object.prototype.hasOwnProperty.call(attributes, attrName)) {
(el as unknown as Record<string, unknown>)[attrName] = attributes[attrName];
}
}
return el;
}
/**
* Append one or several elements to the parent
*
* @param {Element|DocumentFragment} parent - where to append
* @param {Element|Element[]} elements - element or elements list
*/
private append(parent: Element | DocumentFragment, elements: Element | Element[] | DocumentFragment): void {
if (Array.isArray(elements)) {
elements.forEach((el) => parent.appendChild(el));
} else {
parent.appendChild(elements);
}
}
/**
* Append element or a couple to the beginning of the parent elements
*
* @param {Element} parent - where to append
* @param {Element|Element[]} elements - element or elements list
*/
private prepend(parent: Element, elements: Element | Element[]): void {
if (Array.isArray(elements)) {
const reversed = elements.reverse();
reversed.forEach((el) => parent.prepend(el));
} else {
parent.prepend(elements);
}
}
}
/**
* Shows tooltip on element with passed HTML content
* Get singleton tooltip instance
*/
const getTooltip = (): Tooltip => {
return Tooltip.getInstance();
};
/**
* Show tooltip near element with specified content
*
* @param {HTMLElement} element - any HTML element in DOM
* @param content - tooltip's content
* @param options - showing settings
* @param {HTMLElement} element - target element to place tooltip near
* @param {TooltipContent} content - tooltip content
* @param {TooltipOptions} options - tooltip options
*/
export function show(element: HTMLElement, content: TooltipContent, options?: TooltipOptions): void {
prepare();
lib?.show(element, content, options);
}
export const show = (element: HTMLElement, content: TooltipContent, options?: TooltipOptions): void => {
getTooltip().show(element, content, options ?? {});
};
/**
* Hides tooltip
* Hide tooltip
*
* @param skipHidingDelay pass true to immediately hide the tooltip
* @param {boolean} skipHidingDelay - forces hiding immediately
*/
export function hide(skipHidingDelay = false): void {
prepare();
lib?.hide(skipHidingDelay);
}
export const hide = (skipHidingDelay = false): void => {
getTooltip().hide(skipHidingDelay);
};
/**
* Binds 'mouseenter' and 'mouseleave' events that shows/hides the Tooltip
* Show tooltip on hover (mouseenter/mouseleave)
*
* @param {HTMLElement} element - any HTML element in DOM
* @param content - tooltip's content
* @param options - showing settings
* @param {HTMLElement} element - target element to place tooltip near
* @param {TooltipContent} content - tooltip content
* @param {TooltipOptions} options - tooltip options
*/
export function onHover(element: HTMLElement, content: TooltipContent, options?: TooltipOptions): void {
prepare();
lib?.onHover(element, content, options);
}
export const onHover = (element: HTMLElement, content: TooltipContent, options?: TooltipOptions): void => {
getTooltip().onHover(element, content, options ?? {});
};
/**
* Release the library
* Destroy tooltip instance and clean up
*/
export function destroy(): void {
lib?.destroy();
lib = null;
}
export const destroy = (): void => {
getTooltip().destroy();
};

105
src/styles/tooltip.css Normal file
View file

@ -0,0 +1,105 @@
:root {
--ct-color-bg: #1d202b;
--ct-color-font: #cdd1e0;
}
.ct {
position: absolute;
z-index: 999;
top: 0;
left: 0;
opacity: 0;
user-select: none;
pointer-events: none;
transition: opacity 50ms ease-in, transform 70ms cubic-bezier(0.215, 0.61, 0.355, 1);
will-change: opacity, top, left;
box-shadow: 0 8px 12px 0 rgba(29, 32, 43, 0.17), 0 4px 5px -3px rgba(5, 6, 12, 0.49);
border-radius: 9px;
}
.ct::before {
content: '';
inset: 0;
position: absolute;
background-color: var(--ct-color-bg);
z-index: -1;
border-radius: 4px;
}
@supports (-webkit-mask-box-image: url('')) {
.ct::before {
border-radius: 0;
-webkit-mask-box-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M10.71 0h2.58c3.02 0 4.64.42 6.1 1.2a8.18 8.18 0 013.4 3.4C23.6 6.07 24 7.7 24 10.71v2.58c0 3.02-.42 4.64-1.2 6.1a8.18 8.18 0 01-3.4 3.4c-1.47.8-3.1 1.21-6.11 1.21H10.7c-3.02 0-4.64-.42-6.1-1.2a8.18 8.18 0 01-3.4-3.4C.4 17.93 0 16.3 0 13.29V10.7c0-3.02.42-4.64 1.2-6.1a8.18 8.18 0 013.4-3.4C6.07.4 7.7 0 10.71 0z" fill="#000" fill-rule="nonzero"/></svg>') 48% 41% 37.9% 53.3%;
}
}
@media (max-width: 768px) {
.ct {
display: none;
}
}
.ct__content {
padding: 6px 10px;
color: var(--ct-color-font);
font-size: 12px;
text-align: center;
letter-spacing: 0.02em;
line-height: 1em;
}
.ct::after {
content: '';
width: 8px;
height: 8px;
position: absolute;
background-color: var(--ct-color-bg);
z-index: -1;
}
.ct--bottom {
transform: translateY(5px);
}
.ct--bottom::after {
top: -3px;
left: 50%;
transform: translateX(-50%) rotate(-45deg);
}
.ct--top {
transform: translateY(-5px);
}
.ct--top::after {
top: auto;
bottom: -3px;
left: 50%;
transform: translateX(-50%) rotate(-45deg);
}
.ct--left {
transform: translateX(-5px);
}
.ct--left::after {
top: 50%;
left: auto;
right: 0;
transform: translate(41.6%, -50%) rotate(-45deg);
}
.ct--right {
transform: translateX(5px);
}
.ct--right::after {
top: 50%;
left: 0;
transform: translate(-41.6%, -50%) rotate(-45deg);
}
.ct--shown {
opacity: 1;
transform: none;
}

View file

@ -1,6 +0,0 @@
/**
* CSS selector for the main editor wrapper element
* Used in Cypress tests to query the editor container
*/
export const EDITOR_SELECTOR = '[data-interface=editorjs]';

View file

@ -20,7 +20,7 @@ import './commands';
/**
* Test constants
*/
export { EDITOR_SELECTOR } from './constants';
export { EDITOR_INTERFACE_SELECTOR } from '../../../src/components/constants';
/**
* File with custom assertions

View file

@ -3,7 +3,7 @@ import type { ConversionConfig, ToolboxConfig, ToolConfig, API, BlockAPI } from
import type { BlockTuneData } from '../../../../types/block-tunes/block-tune-data';
import ToolMock, { type MockToolData } from '../../fixtures/tools/ToolMock';
import { nanoid } from 'nanoid';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
/**
* There will be described test cases of 'blocks.*' API
@ -74,7 +74,7 @@ describe('api.blocks', () => {
await editor.blocks.update(idToUpdate, newBlockData);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('[data-cy="block-wrapper"]')
.should('have.text', newBlockData.text);
});
@ -213,7 +213,7 @@ describe('api.blocks', () => {
expect(error.message).to.be.eq(`Block with id "${idToUpdate}" not found`);
})
.finally(() => {
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('[data-cy="block-wrapper"]')
.invoke('text')
.then(blockText => {
@ -276,7 +276,7 @@ describe('api.blocks', () => {
},
], index)); // paste to the 0 index
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('[data-cy="block-wrapper"]')
.each(($el, i) => {
switch (i) {

View file

@ -1,6 +1,6 @@
import { createParagraphMock } from '../../support/utils/createParagraphMock';
import type EditorJS from '../../../../types';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
/**
* Test cases for Caret API
@ -27,7 +27,7 @@ describe('Caret API', () => {
/**
* Blur caret from the block before setting via api
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.click();
});
it('should set caret to a block (and return true) if block index is passed as argument', () => {
@ -47,7 +47,7 @@ describe('Caret API', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.first()
.should(($block) => {
@ -76,7 +76,7 @@ describe('Caret API', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.first()
.should(($block) => {
@ -110,7 +110,7 @@ describe('Caret API', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.first()
.should(($block) => {

View file

@ -2,7 +2,7 @@
* There will be described test cases of 'api.toolbar.*' API
*/
import type EditorJS from '../../../../types';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('api.toolbar', () => {
/**
@ -36,7 +36,7 @@ describe('api.toolbar', () => {
describe('*.toggleToolbox()', () => {
const isToolboxVisible = (): void => {
cy.get(EDITOR_SELECTOR).find('div.ce-toolbox')
cy.get(EDITOR_INTERFACE_SELECTOR).find('div.ce-toolbox')
.then((toolbox) => {
if (toolbox.is(':visible')) {
assert.isOk(true, 'Toolbox visible');
@ -47,7 +47,7 @@ describe('api.toolbar', () => {
};
const isToolboxNotVisible = (): void => {
cy.get(EDITOR_SELECTOR).find('div.ce-toolbox')
cy.get(EDITOR_INTERFACE_SELECTOR).find('div.ce-toolbox')
.then((toolbox) => {
if (!toolbox.is(':visible')) {
assert.isOk(true, 'Toolbox not visible');

View file

@ -1,7 +1,7 @@
import type { ToolboxConfig, BlockToolData, ToolboxConfigEntry, PasteConfig } from '../../../../types';
import type EditorJS from '../../../../types';
import type { HTMLPasteEvent, TunesMenuConfig } from '../../../../types/tools';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
/* eslint-disable @typescript-eslint/no-empty-function */
@ -31,19 +31,19 @@ describe('Editor Tools Api', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=testTool]')
.should('have.length', 1);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=testTool] .ce-popover-item__icon')
.should('contain.html', TestTool.toolbox.icon);
});
@ -76,24 +76,24 @@ describe('Editor Tools Api', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=testTool]')
.should('have.length', 2);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=testTool]')
.first()
.should('contain.text', (TestTool.toolbox as ToolboxConfigEntry[])[0].title);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=testTool]')
.last()
.should('contain.text', (TestTool.toolbox as ToolboxConfigEntry[])[1].title);
@ -165,19 +165,19 @@ describe('Editor Tools Api', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=testTool]')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.last()
.click()
@ -238,26 +238,26 @@ describe('Editor Tools Api', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
// Insert test tool block
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get(`[data-item-name="testTool"]`)
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-name=testBlock]')
.type('some text')
.click();
// Open block tunes
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
@ -316,26 +316,26 @@ describe('Editor Tools Api', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
// Insert test tool block
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get(`[data-item-name="testTool"]`)
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-name=testBlock]')
.type('some text')
.click();
// Open block tunes
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
@ -386,31 +386,31 @@ describe('Editor Tools Api', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
// Insert test tool block
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get(`[data-item-name="testTool"]`)
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-name=testBlock]')
.type('some text')
.click();
// Open block tunes
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
// Expect preconfigured custom html tunes to exist in tunes menu
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover')
.should('contain.text', sampleText);
});
@ -468,26 +468,26 @@ describe('Editor Tools Api', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
// Insert test tool block
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get(`[data-item-name="testTool"]`)
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-name=testBlock]')
.type('some text')
.click();
// Open block tunes
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
@ -537,7 +537,7 @@ describe('Editor Tools Api', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -594,7 +594,7 @@ describe('Editor Tools Api', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -660,7 +660,7 @@ describe('Editor Tools Api', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -746,7 +746,7 @@ describe('Editor Tools Api', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -827,7 +827,7 @@ describe('Editor Tools Api', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -918,7 +918,7 @@ describe('Editor Tools Api', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -1001,7 +1001,7 @@ describe('Editor Tools Api', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({

View file

@ -1,5 +1,5 @@
import type { TunesMenuConfig } from '../../../../types/tools';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
/* eslint-disable @typescript-eslint/no-empty-function */
@ -32,12 +32,12 @@ describe('Editor Tunes Api', () => {
tunes: [ 'testTune' ],
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.type('some text')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
@ -80,12 +80,12 @@ describe('Editor Tunes Api', () => {
tunes: [ 'testTune' ],
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.type('some text')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
@ -121,16 +121,16 @@ describe('Editor Tunes Api', () => {
tunes: [ 'testTune' ],
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.type('some text')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover')
.should('contain.text', sampleText);
});
@ -173,12 +173,12 @@ describe('Editor Tunes Api', () => {
tunes: [ 'testTune' ],
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.type('some text')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
@ -218,23 +218,23 @@ describe('Editor Tunes Api', () => {
tunes: [ 'testTune' ],
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.type('some text')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check test tune is inserted at index 0 */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-settings .ce-popover-item')
.eq(0)
.should('have.attr', 'data-item-name', 'test-tune' );
/** Check default Move Up tune is inserted below the test tune */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-settings .ce-popover-item')
.eq(1)
.should('have.attr', 'data-item-name', 'move-up' );

View file

@ -1,7 +1,7 @@
import Header from '@editorjs/header';
import { nanoid } from 'nanoid';
import type EditorJS from '../../../types/index';
import { EDITOR_SELECTOR } from '../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../src/components/constants';
describe('Block ids', () => {
@ -12,7 +12,7 @@ describe('Block ids', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('First block ')
@ -22,15 +22,15 @@ describe('Block ids', () => {
.type('Second block ')
.type('{enter}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=header]')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.last()
.click()
@ -108,7 +108,7 @@ describe('Block ids', () => {
blocks,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.first()
.click()
@ -152,7 +152,7 @@ describe('Block ids', () => {
blocks,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.each(($block, index) => {
expect($block.attr('data-id')).to.eq(blocks[index].id);

View file

@ -2,7 +2,7 @@ import Header from '@editorjs/header';
import Image from '@editorjs/simple-image';
import * as _ from '../../../src/components/utils';
import type { BlockTool, BlockToolData, OutputData } from '../../../types';
import { EDITOR_SELECTOR } from '../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../src/components/constants';
import $ from '../../../src/components/dom';
import type EditorJS from '../../../types/index';
@ -12,7 +12,7 @@ describe.skip('Copy pasting from Editor', () => {
it('should paste plain text', () => {
cy.createEditor({});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.as('block')
.click()
@ -27,7 +27,7 @@ describe.skip('Copy pasting from Editor', () => {
it('should paste inline html data', () => {
cy.createEditor({});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.as('block')
.click()
@ -42,7 +42,7 @@ describe.skip('Copy pasting from Editor', () => {
it('should paste several blocks if plain text contains new lines', () => {
cy.createEditor({});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -50,7 +50,7 @@ describe.skip('Copy pasting from Editor', () => {
'text/plain': 'First block\n\nSecond block',
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.then(blocks => {
expect(blocks[0].textContent).to.eq('First block');
@ -61,7 +61,7 @@ describe.skip('Copy pasting from Editor', () => {
it('should paste several blocks if html contains several paragraphs', () => {
cy.createEditor({});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -69,7 +69,7 @@ describe.skip('Copy pasting from Editor', () => {
'text/html': '<p>First block</p><p>Second block</p>',
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.then(blocks => {
expect(blocks[0].textContent).to.eq('First block');
@ -80,7 +80,7 @@ describe.skip('Copy pasting from Editor', () => {
it('should paste using custom data type', () => {
cy.createEditor({});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -101,7 +101,7 @@ describe.skip('Copy pasting from Editor', () => {
]),
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.then(blocks => {
expect(blocks[0].textContent).to.eq('First block');
@ -116,7 +116,7 @@ describe.skip('Copy pasting from Editor', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -127,11 +127,11 @@ describe.skip('Copy pasting from Editor', () => {
/**
* Check inserted blocks
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('h2.ce-header')
.should('contain', 'First block');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-paragraph')
.should('contain', 'Second block');
@ -165,7 +165,7 @@ describe.skip('Copy pasting from Editor', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.paste({
@ -173,7 +173,7 @@ describe.skip('Copy pasting from Editor', () => {
'text/plain': 'https://codex.so/public/app/img/external/codex2x.png',
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
// In Edge test are performed slower, so we need to increase timeout to wait until image is loaded on the page
.get('img', { timeout: 10000 })
.should('have.attr', 'src', 'https://codex.so/public/app/img/external/codex2x.png');
@ -246,7 +246,7 @@ describe.skip('Copy pasting from Editor', () => {
it('should copy inline fragment', () => {
cy.createEditor({});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('Some text{selectall}')
@ -262,12 +262,12 @@ describe.skip('Copy pasting from Editor', () => {
it('should copy several blocks', () => {
cy.createEditor({});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('First block{enter}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.next()
.type('Second block')
@ -302,7 +302,7 @@ describe.skip('Copy pasting from Editor', () => {
it('should cut inline fragment', () => {
cy.createEditor({});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('Some text{selectall}')
@ -331,7 +331,7 @@ describe.skip('Copy pasting from Editor', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.last()
.click()
@ -360,7 +360,7 @@ describe.skip('Copy pasting from Editor', () => {
});
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.should('not.contain', 'First block')
.should('not.contain', 'Second block');
});
@ -384,7 +384,7 @@ describe.skip('Copy pasting from Editor', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.first()
.click()

View file

@ -1,6 +1,6 @@
// eslint-disable-next-line spaced-comment, @typescript-eslint/triple-slash-reference
/// <reference path="../support/index.d.ts" />
import { EDITOR_SELECTOR } from '../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../src/components/constants';
describe('Editor basic initialization', () => {
describe('Zero-config initialization', () => {
@ -23,7 +23,7 @@ describe('Editor basic initialization', () => {
/**
* Assert if created instance is visible or not.
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.codex-editor')
.should('be.visible');
});
@ -42,7 +42,7 @@ describe('Editor basic initialization', () => {
readOnly: true,
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.codex-editor')
.get('div.ce-paragraph')
.invoke('attr', 'contenteditable')
@ -59,7 +59,7 @@ describe('Editor basic initialization', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('#editor-js-styles')
.should('have.attr', 'nonce', 'test-nonce');
});

View file

@ -1,6 +1,6 @@
import { createEditorWithTextBlocks } from '../../../support/utils/createEditorWithTextBlocks';
import ContentlessToolMock from '../../../fixtures/tools/ContentlessTool';
import { EDITOR_SELECTOR } from '../../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../../src/components/constants';
describe('Arrow Left', () => {
describe('starting whitespaces handling', () => {
@ -10,7 +10,7 @@ describe('Arrow Left', () => {
'&nbsp;2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last() // select second block
.click()
@ -30,7 +30,7 @@ describe('Arrow Left', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -44,7 +44,7 @@ describe('Arrow Left', () => {
' 2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last() // select second block
.click()
@ -63,7 +63,7 @@ describe('Arrow Left', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -78,7 +78,7 @@ describe('Arrow Left', () => {
'<b></b>2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last() // select second block
.click()
@ -97,7 +97,7 @@ describe('Arrow Left', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -111,7 +111,7 @@ describe('Arrow Left', () => {
'<b></b>&nbsp;2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.click()
@ -131,7 +131,7 @@ describe('Arrow Left', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -146,7 +146,7 @@ describe('Arrow Left', () => {
'<b></b>&nbsp;2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.click()
@ -166,7 +166,7 @@ describe('Arrow Left', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -181,7 +181,7 @@ describe('Arrow Left', () => {
' &nbsp;2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.click()
@ -201,7 +201,7 @@ describe('Arrow Left', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -256,7 +256,7 @@ describe('Arrow Left', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.as('thirdBlock')
@ -267,7 +267,7 @@ describe('Arrow Left', () => {
/**
* We navigated to the Delimiter and it is highlighted
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div[data-cy-type=contentless-tool]')
.parents('.ce-block')
.as('delimiterBlock')
@ -297,7 +297,7 @@ describe('Arrow Left', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.should(($block) => {

View file

@ -1,6 +1,6 @@
import { createEditorWithTextBlocks } from '../../../support/utils/createEditorWithTextBlocks';
import ContentlessToolMock from '../../../fixtures/tools/ContentlessTool';
import { EDITOR_SELECTOR } from '../../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../../src/components/constants';
describe('Arrow Right', () => {
describe('starting whitespaces handling', () => {
@ -10,7 +10,7 @@ describe('Arrow Right', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first() // select first block
.as('firstBlock')
@ -32,7 +32,7 @@ describe('Arrow Right', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should(($block) => {
@ -48,7 +48,7 @@ describe('Arrow Right', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -68,7 +68,7 @@ describe('Arrow Right', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should(($block) => {
@ -84,7 +84,7 @@ describe('Arrow Right', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -104,7 +104,7 @@ describe('Arrow Right', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should(($block) => {
@ -119,7 +119,7 @@ describe('Arrow Right', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -140,7 +140,7 @@ describe('Arrow Right', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should(($block) => {
@ -156,7 +156,7 @@ describe('Arrow Right', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -177,7 +177,7 @@ describe('Arrow Right', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should(($block) => {
@ -193,7 +193,7 @@ describe('Arrow Right', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -214,7 +214,7 @@ describe('Arrow Right', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should(($block) => {
@ -270,7 +270,7 @@ describe('Arrow Right', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.as('firstBlock')
@ -280,7 +280,7 @@ describe('Arrow Right', () => {
/**
* We navigated to the Delimiter and it is highlighted
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div[data-cy-type=contentless-tool]')
.parents('.ce-block')
.as('delimiterBlock')
@ -310,7 +310,7 @@ describe('Arrow Right', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should(($block) => {

View file

@ -1,6 +1,6 @@
import type EditorJS from '../../../../../types/index';
import { createEditorWithTextBlocks } from '../../../support/utils/createEditorWithTextBlocks';
import { EDITOR_SELECTOR } from '../../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../../src/components/constants';
describe('Delete keydown', () => {
describe('ending whitespaces handling', () => {
@ -10,7 +10,7 @@ describe('Delete keydown', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -27,7 +27,7 @@ describe('Delete keydown', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -35,7 +35,7 @@ describe('Delete keydown', () => {
.type('{rightArrow}') // set caret after "1";
.type('{del}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.last()
.should('have.text', '1 2');
@ -46,7 +46,7 @@ describe('Delete keydown', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -54,7 +54,7 @@ describe('Delete keydown', () => {
.type('{rightArrow}') // set caret after "1";
.type('{del}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.last()
.should('have.text', '12');
@ -65,7 +65,7 @@ describe('Delete keydown', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -74,7 +74,7 @@ describe('Delete keydown', () => {
.type('{del}') // remove nbsp
.type('{del}'); // ignore empty tag and merge
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.last()
.should('have.text', '12');
@ -86,7 +86,7 @@ describe('Delete keydown', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -95,7 +95,7 @@ describe('Delete keydown', () => {
.type('{del}') // remove nbsp
.type('{del}'); // ignore empty tag and merge
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.last()
.should('have.text', '12');
@ -107,7 +107,7 @@ describe('Delete keydown', () => {
'2',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -116,7 +116,7 @@ describe('Delete keydown', () => {
.type('{del}') // remove nbsp
.type('{del}'); // ignore regular space and merge
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.last()
/**
@ -141,14 +141,14 @@ describe('Delete keydown', () => {
'The second block',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
.selectText('The ')
.type('{del}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.first()
.should('have.text', 'first block');
@ -160,14 +160,14 @@ describe('Delete keydown', () => {
'The second block',
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click() // caret will be at the end of the block
.type('{leftarrow}') // now caret is not at the end
.type('{del}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.first()
.should('have.text', 'The first bloc'); // last char is removed
@ -217,14 +217,14 @@ describe('Delete keydown', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('[data-cy=quote-tool]')
.find('div[contenteditable]')
.first()
.click()
.type('{del}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('[data-cy=quote-tool]')
.find('div[contenteditable]')
.last()
@ -267,7 +267,7 @@ describe('Delete keydown', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -304,7 +304,7 @@ describe('Delete keydown', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -334,7 +334,7 @@ describe('Delete keydown', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -345,7 +345,7 @@ describe('Delete keydown', () => {
/**
* Toolbox has been closed
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-toolbar')
.should('not.have.class', 'ce-toolbar--opened');
});
@ -372,7 +372,7 @@ describe('Delete keydown', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -402,7 +402,7 @@ describe('Delete keydown', () => {
}
const range = selection.getRangeAt(0);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -414,7 +414,7 @@ describe('Delete keydown', () => {
/**
* Toolbox has been closed
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-toolbar')
.should('not.have.class', 'ce-toolbar--opened');
});
@ -464,12 +464,12 @@ describe('Delete keydown', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('{del}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('[data-cy=unmergeable-tool]')
.as('secondBlock');
@ -495,12 +495,12 @@ describe('Delete keydown', () => {
it('should do nothing', () => {
createEditorWithTextBlocks([ 'The only block. Not empty' ]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('{del}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.should('have.length', 1)
.should('have.text', 'The only block. Not empty');

View file

@ -1,4 +1,4 @@
import { EDITOR_SELECTOR } from '../../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../../src/components/constants';
describe('Enter keydown', () => {
it('should split block and remove selected fragment if some text fragment selected', () => {
@ -15,14 +15,14 @@ describe('Enter keydown', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.selectText('with so')
.wait(0)
.type('{enter}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.then((blocks) => {
/**
@ -52,12 +52,12 @@ describe('Enter keydown', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('{enter}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.last()
.as('lastBlock');

View file

@ -1,4 +1,4 @@
import { EDITOR_SELECTOR } from '../../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../../src/components/constants';
describe('Slash keydown', () => {
describe('pressed in empty block', () => {
@ -16,7 +16,7 @@ describe('Slash keydown', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('/');
@ -24,7 +24,7 @@ describe('Slash keydown', () => {
/**
* Block content should contain slash
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.invoke('text')
.should('eq', '/');
@ -51,7 +51,7 @@ describe('Slash keydown', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type(`{${key}}/`);
@ -77,7 +77,7 @@ describe('Slash keydown', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('/');
@ -88,7 +88,7 @@ describe('Slash keydown', () => {
/**
* Block content should contain slash
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.invoke('text')
.should('eq', 'Hello/');
@ -120,10 +120,10 @@ describe('Slash keydown', () => {
// Step 1
// Click on the plus button and select the text option
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-toolbar__plus')
.click({ force: true });
cy.get('[data-cy="toolbox"] .ce-popover__container')
@ -165,7 +165,7 @@ describe('CMD+Slash keydown', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('{cmd}/');

View file

@ -1,7 +1,7 @@
import Header from '@editorjs/header';
import NestedEditor, { NESTED_EDITOR_ID } from '../../support/utils/nestedEditorInstance';
import type { MenuConfig, ToolConstructable } from '@/types/tools';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('Inline Toolbar', () => {
it('should appear aligned with left coord of selection rect', () => {
@ -18,7 +18,7 @@ describe('Inline Toolbar', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectText('block');
@ -58,7 +58,7 @@ describe('Inline Toolbar', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.as('blockWrapper')
.getLineWrapPositions()
@ -68,7 +68,7 @@ describe('Inline Toolbar', () => {
/**
* Select last 5 chars of the first line
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectTextByOffset([firstLineWrapIndex - 5, firstLineWrapIndex - 1]);
});
@ -136,11 +136,11 @@ describe('Inline Toolbar', () => {
});
/** Open Inline Toolbar */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-header')
.selectText('block');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=inline-toolbar]')
.get('.ce-popover--opened')
.as('toolbar')
@ -183,7 +183,7 @@ describe('Inline Toolbar', () => {
form.appendChild(editorElement);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectText('Some text');
@ -214,19 +214,19 @@ describe('Inline Toolbar', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectText('Some text');
cy.get('[data-item-name=convert-to]')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-inline-toolbar')
.find('.ce-popover-item[data-item-name=header]')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-header')
.should('have.text', 'Some text');
@ -242,7 +242,7 @@ describe('Inline Toolbar', () => {
throw new Error('No range available');
}
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-header')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;

View file

@ -1,7 +1,7 @@
import ToolMock, { type MockToolData } from '../../fixtures/tools/ToolMock';
import type EditorJS from '../../../../types/index';
import type { BlockToolConstructorOptions } from '../../../../types';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('Renderer module', () => {
it('should not cause onChange firing during initial rendering', () => {
@ -59,11 +59,11 @@ describe('Renderer module', () => {
})
.as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.should('have.length', 3);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.each(($el, index) => {
/**
@ -125,11 +125,11 @@ describe('Renderer module', () => {
})
.as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.should('have.length', 3);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.each(($el, index) => {
/**
@ -158,7 +158,7 @@ describe('Renderer module', () => {
})
.as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.should('have.length', 1);
});
@ -174,7 +174,7 @@ describe('Renderer module', () => {
});
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.should('have.length', 1);
});

View file

@ -1,6 +1,6 @@
import type EditorJS from '../../../../types/index';
import Header from '@editorjs/header';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('Saver module', () => {
describe('save()', () => {
@ -22,7 +22,7 @@ describe('Saver module', () => {
*/
const extensionNode = document.createElement('extension-node');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block__content')
.then((blockContent) => {
blockContent.append(extensionNode);
@ -58,18 +58,18 @@ describe('Saver module', () => {
})
.as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('span.ce-toolbar__settings-btn')
.click();
/**
* Change header level
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-settings .ce-popover-item:nth-child(3)')
.click();

View file

@ -1,6 +1,6 @@
import { createEditorWithTextBlocks } from '../../support/utils/createEditorWithTextBlocks';
import type EditorJS from '../../../../types/index';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('Ui module', () => {
describe('documentKeydown', () => {
@ -30,7 +30,7 @@ describe('Ui module', () => {
/**
* Select two blocks by shift+down
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -76,7 +76,7 @@ describe('Ui module', () => {
/**
* Select two blocks by shift+down
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.click()
@ -105,7 +105,7 @@ describe('Ui module', () => {
])
.as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.eq(1)
.click();
@ -128,7 +128,7 @@ describe('Ui module', () => {
})
.as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.eq(1)
.click();

View file

@ -2,7 +2,7 @@ import Header from '@editorjs/header';
import Code from '@editorjs/code';
import ToolMock from '../fixtures/tools/ToolMock';
import Delimiter from '@editorjs/delimiter';
import { EDITOR_SELECTOR } from '../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../src/components/constants';
import { BlockAddedMutationType } from '../../../types/events/block/BlockAdded';
import { BlockChangedMutationType } from '../../../types/events/block/BlockChanged';
import { BlockRemovedMutationType } from '../../../types/events/block/BlockRemoved';
@ -82,7 +82,7 @@ describe('onChange callback', () => {
},
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('change')
@ -114,7 +114,7 @@ describe('onChange callback', () => {
},
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('first change')
@ -137,7 +137,7 @@ describe('onChange callback', () => {
it('should be fired with correct index on block insertion above the current (by pressing Enter at the start)', () => {
createEditor();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('{enter}');
@ -161,7 +161,7 @@ describe('onChange callback', () => {
},
} ]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('{enter}');
@ -180,7 +180,7 @@ describe('onChange callback', () => {
},
} ]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('{enter}');
@ -196,7 +196,7 @@ describe('onChange callback', () => {
it('should be fired on typing into block', () => {
createEditor();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('some text');
@ -212,15 +212,15 @@ describe('onChange callback', () => {
it('should be fired on block insertion with save inside onChange', () => {
createEditorWithSave();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=delimiter]')
.click();
@ -258,15 +258,15 @@ describe('onChange callback', () => {
it('should be fired on block replacement for both of blocks', () => {
createEditor();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-toolbar__plus')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item[data-item-name=header]')
.click();
@ -302,15 +302,15 @@ describe('onChange callback', () => {
},
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('span.ce-toolbar__settings-btn')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-settings .ce-popover-item:nth-child(4)')
.click();
@ -335,20 +335,20 @@ describe('onChange callback', () => {
},
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('span.ce-toolbar__settings-btn')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name=delete]')
.click();
/** Second click for confirmation */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name=delete]')
.click();
@ -390,16 +390,16 @@ describe('onChange callback', () => {
},
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.last()
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('span.ce-toolbar__settings-btn')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name=move-up]')
.click();
@ -420,7 +420,7 @@ describe('onChange callback', () => {
},
} ]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('textarea')
.type('Some input to the textarea');
@ -440,21 +440,21 @@ describe('onChange callback', () => {
},
} ]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
/**
* Open Block Tunes, add fake cursor
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('span.ce-toolbar__settings-btn')
.click();
/**
* Close Block Tunes, remove fake cursor
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click();
@ -471,7 +471,7 @@ describe('onChange callback', () => {
},
} ]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('div.ce-block')
.click()
.type('{backspace}');
@ -673,7 +673,7 @@ describe('onChange callback', () => {
/**
* Emulate tool's child-element text typing
*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=tool-child]')
.click()
.type('some text');

View file

@ -1,5 +1,5 @@
import * as _ from '../../../src/components/utils';
import { EDITOR_SELECTOR } from '../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../src/components/constants';
describe('Blocks selection', () => {
beforeEach(() => {
@ -13,12 +13,12 @@ describe('Blocks selection', () => {
});
it('should remove block selection on click', () => {
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.click()
.type('First block{enter}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('div.ce-block')
.next()
.type('Second block')
@ -28,7 +28,7 @@ describe('Blocks selection', () => {
keyCode: _.keyCodes.UP,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.click()
.find('div.ce-block')
.should('not.have.class', '.ce-block--selected');

View file

@ -3,7 +3,7 @@ import Header from '@editorjs/header';
import type { ConversionConfig, ToolboxConfig } from '../../../../types';
import type { MenuConfig } from '../../../../types/tools';
import { ToolWithoutConversionExport } from '../../fixtures/tools/ToolWithoutConversionExport';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('BlockTunes', () => {
describe('Keyboard only', () => {
@ -23,7 +23,7 @@ describe('BlockTunes', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('{cmd}/')
@ -53,7 +53,7 @@ describe('BlockTunes', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('{cmd}/')
@ -88,28 +88,28 @@ describe('BlockTunes', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check "Convert to" option is present */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item')
.contains('Convert to')
.should('exist');
/** Click "Convert to" option*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item')
.contains('Convert to')
.click();
/** Check connected popover with the "Heading" option is present */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover--nested [data-item-name=header]')
.should('exist');
});
@ -130,16 +130,16 @@ describe('BlockTunes', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check "Convert to" option is not present */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item')
.contains('Convert to')
.should('not.exist');
@ -163,17 +163,17 @@ describe('BlockTunes', () => {
}).as('editorInstance');
cy.get('@editorInstance')
.get(EDITOR_SELECTOR)
.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-block')
.click();
cy.get('@editorInstance')
.get(EDITOR_SELECTOR)
.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-toolbar__settings-btn')
.click();
cy.get('@editorInstance')
.get(EDITOR_SELECTOR)
.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-popover-item[data-item-name=convert-to]')
.should('not.exist');
});
@ -256,28 +256,28 @@ describe('BlockTunes', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Open "Convert to" menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item')
.contains('Convert to')
.click();
/** Check TestTool option with SAME data is NOT present */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover--nested [data-item-name=testTool]')
.contains('Title 1')
.should('not.exist');
/** Check TestTool option with DIFFERENT data IS present */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover--nested [data-item-name=testTool]')
.contains('Title 2')
.should('exist');
@ -301,27 +301,27 @@ describe('BlockTunes', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Click "Convert to" option*/
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item')
.contains('Convert to')
.click();
/** Click "Heading" option */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover--nested [data-item-name=header]')
.click();
/** Check the block was converted to the second option */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-header')
.should('have.text', 'Some text');
@ -335,7 +335,7 @@ describe('BlockTunes', () => {
throw new Error('Range is undefined');
}
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-header')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -418,21 +418,21 @@ describe('BlockTunes', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check there are more than 1 tune */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item')
.should('have.length.above', 1);
/** Check the first tune is tool specific tune */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-item:first-child')
.contains('Tune')
.should('exist');

View file

@ -1,5 +1,5 @@
import { createEditorWithTextBlocks } from '../../support/utils/createEditorWithTextBlocks';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('inputs [data-empty] mark', () => {
it('should be added to inputs of editor on initialization', () => {
@ -8,12 +8,12 @@ describe('inputs [data-empty] mark', () => {
'', // empty block
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.first()
.should('have.attr', 'data-empty', 'false');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should('have.attr', 'data-empty', 'true');
@ -25,12 +25,12 @@ describe('inputs [data-empty] mark', () => {
'', // empty block
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.type('Some text');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should('have.attr', 'data-empty', 'false');
@ -42,12 +42,12 @@ describe('inputs [data-empty] mark', () => {
'Some text', // not empty block
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.type('{selectall}{backspace}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should('have.attr', 'data-empty', 'true');
@ -59,12 +59,12 @@ describe('inputs [data-empty] mark', () => {
'', // empty block
]);
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.type('{enter}');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.last()
.should('have.attr', 'data-empty', 'true');

View file

@ -1,7 +1,7 @@
import Header from '@editorjs/header';
import type { InlineTool, InlineToolConstructorOptions, MenuConfig, ToolConstructable } from '../../../../types/tools';
import { createEditorWithTextBlocks } from '../../support/utils/createEditorWithTextBlocks';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('Inline Toolbar', () => {
describe('Separators', () => {
@ -25,18 +25,18 @@ describe('Inline Toolbar', () => {
});
/** Open Inline Toolbar */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectText('block');
/** Check that first item (which is convert-to and has children) has a separator after it */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=inline-toolbar] .ce-popover__items')
.children()
.first()
.should('have.attr', 'data-item-name', 'convert-to');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=inline-toolbar] .ce-popover__items')
.children()
.eq(1)
@ -92,24 +92,24 @@ describe('Inline Toolbar', () => {
});
/** Open Inline Toolbar */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-header')
.selectText('block');
/** Check that item with children is surrounded by separators */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=inline-toolbar] .ce-popover__items')
.children()
.eq(3)
.should('have.class', 'ce-popover-item-separator');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=inline-toolbar] .ce-popover__items')
.children()
.eq(4)
.should('have.attr', 'data-item-name', 'test-tool');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=inline-toolbar] .ce-popover__items')
.children()
.eq(5)
@ -165,18 +165,18 @@ describe('Inline Toolbar', () => {
});
/** Open Inline Toolbar */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-header')
.selectText('block');
/** Check that item with children is surrounded by separators */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=inline-toolbar] .ce-popover__items')
.children()
.eq(3)
.should('have.class', 'ce-popover-item-separator');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-cy=inline-toolbar] .ce-popover__items')
.children()
.eq(4)
@ -214,7 +214,7 @@ describe('Inline Toolbar', () => {
readOnly: true,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectText('text');

View file

@ -1,4 +1,4 @@
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
/**
* Text will be passed as a placeholder to the editor
@ -19,7 +19,7 @@ describe('Placeholders', () => {
placeholder: PLACEHOLDER_TEXT,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.getPseudoElementContent('::before')
.should('eq', PLACEHOLDER_TEXT);
@ -31,7 +31,7 @@ describe('Placeholders', () => {
autofocus: true,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.getPseudoElementContent('::before')
.should('eq', PLACEHOLDER_TEXT);
@ -42,7 +42,7 @@ describe('Placeholders', () => {
placeholder: PLACEHOLDER_TEXT,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.as('firstBlock')
@ -55,7 +55,7 @@ describe('Placeholders', () => {
placeholder: PLACEHOLDER_TEXT,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.type('aaa')
.type('{selectall}{backspace}')
@ -68,7 +68,7 @@ describe('Placeholders', () => {
placeholder: PLACEHOLDER_TEXT,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.as('firstBlock')
.getPseudoElementContent('::before')
@ -85,7 +85,7 @@ describe('Placeholders', () => {
placeholder: PLACEHOLDER_TEXT,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.as('firstBlock')
.getPseudoElementContent('::before')

View file

@ -1,7 +1,7 @@
import type EditorJS from '../../../../types/index';
import type { ConversionConfig, ToolboxConfig } from '../../../../types/index';
import ToolMock from '../../fixtures/tools/ToolMock';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
describe('Toolbox', () => {
describe('Shortcuts', () => {
@ -39,7 +39,7 @@ describe('Toolbox', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('Some text')
@ -68,7 +68,7 @@ describe('Toolbox', () => {
throw new Error('Selection range is not available');
}
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find(`.ce-block[data-id=${blocks[0].id}]`)
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
@ -102,7 +102,7 @@ describe('Toolbox', () => {
},
}).as('editorInstance');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('Some text')
@ -151,7 +151,7 @@ describe('Toolbox', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('Some text')
@ -203,7 +203,7 @@ describe('Toolbox', () => {
},
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.click()
.type('Some text')

View file

@ -1,5 +1,5 @@
import type { PopoverItemParams } from '../../../../types/index.js';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
/**
* Mock of some Block Tool
@ -70,7 +70,7 @@ describe('Flipper', () => {
cy.spy(SomePlugin, 'pluginInternalKeydownHandler');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-some-plugin')
.as('pluginInput')
.focus()
@ -78,7 +78,7 @@ describe('Flipper', () => {
.wait(100);
// Try to delete the block via keyboard
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-some-plugin')
// Open tunes menu
.trigger('keydown', { code: 'Slash',
@ -93,7 +93,7 @@ describe('Flipper', () => {
cy.get('[data-item-name="delete"]')
.should('have.class', 'ce-popover-item--focused');
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-some-plugin')
// Click delete
.trigger('keydown', { keyCode: ENTER_KEY_CODE })
@ -118,7 +118,7 @@ describe('Flipper', () => {
autofocus: true,
});
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-paragraph')
.as('paragraph')
.selectTextByOffset([0, 10])

View file

@ -2,7 +2,7 @@ import { PopoverDesktop as Popover, PopoverItemType } from '../../../../src/comp
import type { PopoverItemParams } from '@/types/utils/popover';
import type { MenuConfig } from '../../../../types/tools';
import type { BlockToolConstructable } from '../../../../types/tools';
import { EDITOR_SELECTOR } from '../../support/constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
import Header from '@editorjs/header';
/* eslint-disable @typescript-eslint/no-empty-function */
@ -285,16 +285,16 @@ describe('Popover', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check item with custom html content is displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover .ce-popover-item-html')
.contains('Tune')
.should('be.visible');
@ -355,11 +355,11 @@ describe('Popover', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
@ -368,7 +368,7 @@ describe('Popover', () => {
cy.get('body').tab();
/** Check the first custom html item is focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover .ce-popover-item-html .ce-settings__button')
.contains('Tune1')
.should('have.class', 'ce-popover-item--focused');
@ -378,7 +378,7 @@ describe('Popover', () => {
cy.get('body').tab();
/** Check the second custom html item is focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover .ce-popover-item-html .ce-settings__button')
.contains('Tune2')
.should('have.class', 'ce-popover-item--focused');
@ -388,7 +388,7 @@ describe('Popover', () => {
cy.get('body').tab();
/** Check that default popover item got focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name=move-up]')
.should('have.class', 'ce-popover-item--focused');
});
@ -438,32 +438,32 @@ describe('Popover', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check item with children has arrow icon */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name="test-item"]')
.get('.ce-popover-item__icon--chevron-right')
.should('be.visible');
/** Click the item */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name="test-item"]')
.click();
/** Check nested popover opened */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover--nested .ce-popover__container')
.should('be.visible');
/** Check child item displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover--nested .ce-popover__container')
.get('[data-item-name="nested-test-item"]')
.should('be.visible');
@ -517,62 +517,62 @@ describe('Popover', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check item with children has arrow icon */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name="test-item"]')
.get('.ce-popover-item__icon--chevron-right')
.should('be.visible');
/** Click the item */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name="test-item"]')
.click();
/** Check child item displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="nested-test-item"]')
.should('be.visible');
/** Check header displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-header')
.should('have.text', 'Tune');
/** Check back button displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('.ce-popover-header__back-button')
.should('be.visible');
/** Click back button */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('.ce-popover-header__back-button')
.click();
/** Check child item is not displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="nested-test-item"]')
.should('not.exist');
/** Check back button is not displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('.ce-popover-header__back-button')
.should('not.exist');
/** Check header is not displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover-header')
.should('not.exist');
});
@ -614,16 +614,16 @@ describe('Popover', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check item displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="test-item"]')
.should('be.visible');
@ -671,22 +671,22 @@ describe('Popover', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
/** Check item displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="test-item"]')
.should('be.visible');
/** Check separator displayed */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('.ce-popover-item-separator')
.should('be.visible');
@ -738,11 +738,11 @@ describe('Popover', () => {
});
/** Open block tunes menu */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.cdx-block')
.click();
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-toolbar__settings-btn')
.click();
@ -751,13 +751,13 @@ describe('Popover', () => {
cy.get('body').tab();
/** Check first item is focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="test-item-1"].ce-popover-item--focused')
.should('exist');
/** Check second item is not focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="test-item-2"].ce-popover-item--focused')
.should('not.exist');
@ -767,13 +767,13 @@ describe('Popover', () => {
cy.get('body').tab();
/** Check first item is not focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="test-item-1"].ce-popover-item--focused')
.should('not.exist');
/** Check second item is focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="test-item-2"].ce-popover-item--focused')
.should('exist');
@ -801,27 +801,27 @@ describe('Popover', () => {
});
/** Open Inline Toolbar */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectText('block');
/** Hover Convert To item which has nested popover */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name=convert-to]')
.trigger('mouseover');
/** Check nested popover didn't open */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover--nested .ce-popover__container')
.should('not.exist');
/** Click Convert To item which has nested popover */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('[data-item-name=convert-to]')
.click();
/** Check nested popover opened */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover--nested .ce-popover__container')
.should('exist');
});
@ -847,17 +847,17 @@ describe('Popover', () => {
});
/** Open Inline Toolbar */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectText('block');
/** Check Inline Popover opened */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-inline-toolbar .ce-popover__container')
.should('be.visible');
/** Check first item is NOT focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-inline-toolbar .ce-popover__container')
.get('[data-item-name="convert-to"].ce-popover-item--focused')
.should('not.exist');
@ -866,13 +866,13 @@ describe('Popover', () => {
cy.tab();
/** Check first item became focused after tab */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-inline-toolbar .ce-popover__container')
.get('[data-item-name="convert-to"].ce-popover-item--focused')
.should('exist');
/** Check second item is NOT focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-inline-toolbar .ce-popover__container')
.get('[data-item-name="link"] .ce-popover-item--focused')
.should('not.exist');
@ -881,7 +881,7 @@ describe('Popover', () => {
cy.tab();
/** Check second item became focused after tab */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-inline-toolbar .ce-popover__container')
.get('[data-item-name="link"] .ce-popover-item--focused')
.should('exist');
@ -908,12 +908,12 @@ describe('Popover', () => {
});
/** Open Inline Toolbar */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.find('.ce-paragraph')
.selectText('block');
/** Check Inline Popover opened */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-inline-toolbar .ce-popover__container')
.should('be.visible');
@ -925,12 +925,12 @@ describe('Popover', () => {
.type('{enter}');
/** Check Inline Popover opened */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-inline-toolbar .ce-popover--nested .ce-popover__container')
.should('be.visible');
/** Check first item is NOT focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="header"].ce-popover-item--focused')
.should('not.exist');
@ -940,7 +940,7 @@ describe('Popover', () => {
cy.get('body').tab();
/** Check first item is focused */
cy.get(EDITOR_SELECTOR)
cy.get(EDITOR_INTERFACE_SELECTOR)
.get('.ce-popover__container')
.get('[data-item-name="header"].ce-popover-item--focused')
.should('exist');

View file

@ -1,6 +0,0 @@
/**
* CSS selector for the main editor wrapper element
* Used in Playwright tests to query the editor container
*/
export const EDITOR_SELECTOR = '[data-interface=editorjs]';

View file

@ -4,20 +4,19 @@ import path from 'node:path';
import { pathToFileURL } from 'node:url';
import type { OutputData } from '@/types';
import { ensureEditorBundleBuilt } from './helpers/ensure-build';
import { EDITOR_SELECTOR } from './constants';
import { TOOLTIP_INTERFACE_SELECTOR, EDITOR_INTERFACE_SELECTOR, INLINE_TOOLBAR_INTERFACE_SELECTOR } from '../../../src/components/constants';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../cypress/fixtures/test.html')
).href;
const HOLDER_ID = 'editorjs';
const BLOCK_SELECTOR = `${EDITOR_SELECTOR} div.ce-block`;
const PARAGRAPH_SELECTOR = `${EDITOR_SELECTOR} [data-block-tool="paragraph"]`;
const SETTINGS_BUTTON_SELECTOR = `${EDITOR_SELECTOR} .ce-toolbar__settings-btn`;
const PLUS_BUTTON_SELECTOR = `${EDITOR_SELECTOR} .ce-toolbar__plus`;
const INLINE_TOOLBAR_SELECTOR = `${EDITOR_SELECTOR} [data-interface=inline-toolbar]`;
const POPOVER_SELECTOR = `${EDITOR_SELECTOR} .ce-popover`;
const TOOLTIP_SELECTOR = '.ct';
const BLOCK_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} div.ce-block`;
const PARAGRAPH_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-block-tool="paragraph"]`;
const SETTINGS_BUTTON_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-toolbar__settings-btn`;
const PLUS_BUTTON_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-toolbar__plus`;
const INLINE_TOOLBAR_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} ${INLINE_TOOLBAR_INTERFACE_SELECTOR}`;
const POPOVER_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-popover`;
const LINK_TOOL_SHORTCUT_MODIFIER = process.platform === 'darwin' ? 'Meta' : 'Control';
/**
@ -215,7 +214,7 @@ const selectText = async (locator: Locator, text: string): Promise<void> => {
const getTooltipText = async (page: Page, triggerElement: Locator): Promise<string> => {
await triggerElement.hover();
const tooltip = page.locator(TOOLTIP_SELECTOR);
const tooltip = page.locator(TOOLTIP_INTERFACE_SELECTOR);
await expect(tooltip).toBeVisible();

View file

@ -5,15 +5,15 @@ import { pathToFileURL } from 'node:url';
import type EditorJS from '@/types';
import type { OutputData } from '@/types';
import { ensureEditorBundleBuilt } from '../helpers/ensure-build';
import { EDITOR_SELECTOR } from '../constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../../cypress/fixtures/test.html')
).href;
const HOLDER_ID = 'editorjs';
const PARAGRAPH_SELECTOR = `${EDITOR_SELECTOR} [data-block-tool="paragraph"]`;
const INLINE_TOOLBAR_SELECTOR = `${EDITOR_SELECTOR} [data-cy=inline-toolbar]`;
const PARAGRAPH_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-block-tool="paragraph"]`;
const INLINE_TOOLBAR_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-cy=inline-toolbar]`;
/**
* Reset the editor holder and destroy any existing instance

View file

@ -5,6 +5,7 @@ import { pathToFileURL } from 'node:url';
import type EditorJS from '@/types';
import type { OutputData } from '@/types';
import { ensureEditorBundleBuilt } from '../helpers/ensure-build';
import { INLINE_TOOLBAR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../../cypress/fixtures/test.html')
@ -12,7 +13,7 @@ const TEST_PAGE_URL = pathToFileURL(
const HOLDER_ID = 'editorjs';
const PARAGRAPH_SELECTOR = '[data-block-tool="paragraph"]';
const INLINE_TOOLBAR_SELECTOR = '[data-interface=inline-toolbar]';
const INLINE_TOOLBAR_SELECTOR = INLINE_TOOLBAR_INTERFACE_SELECTOR;
const LINK_BUTTON_SELECTOR = `${INLINE_TOOLBAR_SELECTOR} [data-item-name="link"] button`;
const LINK_INPUT_SELECTOR = `input[data-link-tool-input-opened]`;
const NOTIFIER_SELECTOR = '.cdx-notifies';

View file

@ -5,14 +5,14 @@ import { pathToFileURL } from 'node:url';
import type EditorJS from '../../../../../types';
import type { OutputData } from '../../../../../types';
import { ensureEditorBundleBuilt } from '../../helpers/ensure-build';
import { EDITOR_SELECTOR } from '../../constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../../src/components/constants';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../../../cypress/fixtures/test.html')
).href;
const BLOCK_SELECTOR = `${EDITOR_SELECTOR} div.ce-block`;
const PARAGRAPH_SELECTOR = `${EDITOR_SELECTOR} [data-block-tool="paragraph"]`;
const TOOLBAR_SELECTOR = `${EDITOR_SELECTOR} .ce-toolbar`;
const BLOCK_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} div.ce-block`;
const PARAGRAPH_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-block-tool="paragraph"]`;
const TOOLBAR_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-toolbar`;
const HOLDER_ID = 'editorjs';
/**
@ -520,7 +520,7 @@ test.describe('Backspace keydown', () => {
test('should navigate to previous input when caret is not at first input', async ({ page }) => {
await createMultiInputToolEditor(page);
const inputs = page.locator(`${EDITOR_SELECTOR} [data-cy=quote-tool] div[contenteditable]`);
const inputs = page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-cy=quote-tool] div[contenteditable]`);
const lastInput = inputs.last();
await lastInput.click();
@ -641,7 +641,7 @@ test.describe('Backspace keydown', () => {
expect(blocks[0].id).toBe('block1');
expect((blocks[0].data as { text: string }).text).toBe('First block headingSecond block paragraph');
await expectCaretOffset(page.locator(`${EDITOR_SELECTOR} [data-cy=block-wrapper]`).first(), 'First block heading'.length, { normalize: true });
await expectCaretOffset(page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-cy=block-wrapper]`).first(), 'First block heading'.length, { normalize: true });
await expectToolbarClosed(page);
});
@ -659,7 +659,7 @@ test.describe('Backspace keydown', () => {
},
]);
const targetBlock = page.locator(`${EDITOR_SELECTOR} [data-cy="block-wrapper"][data-id="block2"]`);
const targetBlock = page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-cy="block-wrapper"][data-id="block2"]`);
await targetBlock.click();
await targetBlock.press('Home');
@ -671,7 +671,7 @@ test.describe('Backspace keydown', () => {
expect(blocks[0].id).toBe('block1');
expect((blocks[0].data as { text: string }).text).toBe('First block paragraphSecond block heading');
await expectCaretOffset(page.locator(`${EDITOR_SELECTOR} [data-cy=block-wrapper]`).first(), 'First block paragraph'.length, { normalize: true });
await expectCaretOffset(page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-cy=block-wrapper]`).first(), 'First block paragraph'.length, { normalize: true });
await expectToolbarClosed(page);
});
@ -687,7 +687,7 @@ test.describe('Backspace keydown', () => {
const { blocks } = await saveEditor(page);
expect(blocks).toHaveLength(2);
await expectCaretAtEnd(page.locator(`${EDITOR_SELECTOR} [data-cy=unmergeable-tool]`));
await expectCaretAtEnd(page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-cy=unmergeable-tool]`));
await expectToolbarClosed(page);
});
@ -703,7 +703,7 @@ test.describe('Backspace keydown', () => {
const { blocks } = await saveEditor(page);
expect(blocks).toHaveLength(2);
await expectCaretAtEnd(page.locator(`${EDITOR_SELECTOR} [data-cy=unmergeable-tool]`));
await expectCaretAtEnd(page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-cy=unmergeable-tool]`));
await expectToolbarClosed(page);
});

View file

@ -5,13 +5,13 @@ import { pathToFileURL } from 'node:url';
import type EditorJS from '../../../../../types';
import type { OutputData } from '../../../../../types';
import { ensureEditorBundleBuilt } from '../../helpers/ensure-build';
import { EDITOR_SELECTOR } from '../../constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../../../src/components/constants';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../../../cypress/fixtures/test.html')
).href;
const HOLDER_ID = 'editorjs';
const PARAGRAPH_SELECTOR = `${EDITOR_SELECTOR} [data-block-tool="paragraph"]`;
const PARAGRAPH_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-block-tool="paragraph"]`;
const TOOL_WITH_TWO_INPUTS_SELECTOR = '[data-cy=tool-with-two-inputs] div[contenteditable=true]';
const CONTENTLESS_TOOL_SELECTOR = '[data-cy=contentless-tool]';
const REGULAR_INPUT_SELECTOR = '[data-cy=regular-input]';

View file

@ -4,14 +4,14 @@ import path from 'node:path';
import { pathToFileURL } from 'node:url';
import type { OutputData } from '@/types';
import { ensureEditorBundleBuilt } from './helpers/ensure-build';
import { EDITOR_SELECTOR } from './constants';
import { EDITOR_INTERFACE_SELECTOR } from '../../../src/components/constants';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../cypress/fixtures/test.html')
).href;
const HOLDER_ID = 'editorjs';
const BLOCK_SELECTOR = `${EDITOR_SELECTOR} div.ce-block`;
const BLOCK_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} div.ce-block`;
/**
* Reset the editor holder and destroy any existing instance
@ -237,7 +237,7 @@ test.describe('Sanitizing', () => {
await selectAllText(block);
// Click bold button
const boldButton = page.locator(`${EDITOR_SELECTOR} [data-item-name="bold"]`);
const boldButton = page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-item-name="bold"]`);
await boldButton.click();
@ -288,7 +288,7 @@ test.describe('Sanitizing', () => {
},
]);
const lastParagraph = page.locator(`${EDITOR_SELECTOR} .ce-paragraph`).last();
const lastParagraph = page.locator(`${EDITOR_INTERFACE_SELECTOR} .ce-paragraph`).last();
await lastParagraph.click();
await page.keyboard.press('Home');
@ -324,7 +324,7 @@ test.describe('Sanitizing', () => {
await selectAllText(block);
const italicButton = page.locator(`${EDITOR_SELECTOR} [data-item-name="italic"]`);
const italicButton = page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-item-name="italic"]`);
await italicButton.click();
await block.click();
@ -359,7 +359,7 @@ test.describe('Sanitizing', () => {
await selectAllText(block);
const linkButton = page.locator(`${EDITOR_SELECTOR} [data-item-name="link"]`);
const linkButton = page.locator(`${EDITOR_INTERFACE_SELECTOR} [data-item-name="link"]`);
await linkButton.click();
@ -721,7 +721,7 @@ test.describe('Sanitizing', () => {
},
]);
const lastParagraph = page.locator(`${EDITOR_SELECTOR} .ce-paragraph`).last();
const lastParagraph = page.locator(`${EDITOR_INTERFACE_SELECTOR} .ce-paragraph`).last();
await lastParagraph.click();
await page.keyboard.press('Home');
@ -751,7 +751,7 @@ test.describe('Sanitizing', () => {
},
]);
const lastParagraph = page.locator(`${EDITOR_SELECTOR} .ce-paragraph`).last();
const lastParagraph = page.locator(`${EDITOR_INTERFACE_SELECTOR} .ce-paragraph`).last();
await lastParagraph.click();
await page.keyboard.press('Home');

View file

@ -4,18 +4,17 @@ import path from 'node:path';
import { pathToFileURL } from 'node:url';
import type { OutputData } from '@/types';
import { PopoverItemType } from '@/types/utils/popover/popover-item-type';
import { selectionChangeDebounceTimeout } from '../../../../src/components/constants';
import { selectionChangeDebounceTimeout, EDITOR_INTERFACE_SELECTOR } from '../../../../src/components/constants';
import { ensureEditorBundleBuilt } from '../helpers/ensure-build';
import { EDITOR_SELECTOR } from '../constants';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../../cypress/fixtures/test.html')
).href;
const HOLDER_ID = 'editorjs';
const BLOCK_SELECTOR = `${EDITOR_SELECTOR} .cdx-block`;
const BLOCK_TUNES_SELECTOR = `${EDITOR_SELECTOR} [data-cy=block-tunes]`;
const SETTINGS_BUTTON_SELECTOR = `${EDITOR_SELECTOR} .ce-toolbar__settings-btn`;
const BLOCK_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .cdx-block`;
const BLOCK_TUNES_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} [data-cy=block-tunes]`;
const SETTINGS_BUTTON_SELECTOR = `${EDITOR_INTERFACE_SELECTOR} .ce-toolbar__settings-btn`;
const SEARCH_INPUT_SELECTOR = `${BLOCK_TUNES_SELECTOR} .cdx-search-field__input`;
const POPOVER_ITEM_SELECTOR = `${BLOCK_TUNES_SELECTOR} .ce-popover-item`;
const NOTHING_FOUND_SELECTOR = `${BLOCK_TUNES_SELECTOR} .ce-popover__nothing-found-message`;

View file

@ -0,0 +1,975 @@
import { expect, test } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import { ensureEditorBundleBuilt } from '../helpers/ensure-build';
import { TOOLTIP_INTERFACE_SELECTOR } from '../../../../src/components/constants';
const TEST_PAGE_URL = pathToFileURL(
path.resolve(__dirname, '../../../cypress/fixtures/test.html')
).href;
const HOLDER_ID = 'editorjs';
/**
* Reset the editor holder and destroy any existing instance
*
* @param page - The Playwright page object
*/
const resetEditor = async (page: Page): Promise<void> => {
await page.evaluate(async ({ holderId }) => {
if (window.editorInstance) {
await window.editorInstance.destroy?.();
window.editorInstance = undefined;
}
document.getElementById(holderId)?.remove();
const container = document.createElement('div');
container.id = holderId;
container.dataset.cy = holderId;
container.style.border = '1px dotted #388AE5';
document.body.appendChild(container);
}, { holderId: HOLDER_ID });
};
/**
* Create editor instance
*
* @param page - The Playwright page object
*/
const createEditor = async (page: Page): Promise<void> => {
await page.waitForFunction(() => typeof window.EditorJS === 'function');
await page.evaluate(async ({ holderId }) => {
const editor = new window.EditorJS({
holder: holderId,
});
window.editorInstance = editor;
await editor.isReady;
}, { holderId: HOLDER_ID });
};
/**
* Wait for tooltip to appear and verify it's visible
*
* @param page - The Playwright page object
* @returns The tooltip locator
*/
const waitForTooltip = async (page: Page): Promise<Locator> => {
const tooltip = page.locator(TOOLTIP_INTERFACE_SELECTOR);
await expect(tooltip).toBeVisible();
return tooltip;
};
/**
* Wait for tooltip to disappear
*
* @param page - The Playwright page object
*/
const waitForTooltipToHide = async (page: Page): Promise<void> => {
const tooltip = page.locator(TOOLTIP_INTERFACE_SELECTOR);
await expect(tooltip).toBeHidden();
};
test.beforeAll(() => {
ensureEditorBundleBuilt();
});
test.beforeEach(async ({ page }) => {
await page.goto(TEST_PAGE_URL);
await resetEditor(page);
await createEditor(page);
});
test.describe('Tooltip API', () => {
test.describe('show()', () => {
test('should show tooltip with text content', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Test Button';
element.id = 'test-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Test tooltip text');
}
}, { elementId: testElement.id });
const tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('Test tooltip text');
});
test('should show tooltip with HTML content', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('div');
element.textContent = 'Hover me';
element.id = 'test-element';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
const htmlContent = document.createElement('div');
htmlContent.innerHTML = '<strong>Bold</strong> tooltip';
editor.tooltip.show(element, htmlContent);
}
}, { elementId: testElement.id });
const tooltip = await waitForTooltip(page);
await expect(tooltip.locator('strong')).toContainText('Bold');
await expect(tooltip).toContainText('tooltip');
});
test('should show tooltip with options', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('span');
element.textContent = 'Test';
element.id = 'test-span';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Tooltip with options', {
placement: 'top',
hidingDelay: 100,
});
}
}, { elementId: testElement.id });
const tooltip = await waitForTooltip(page);
await expect(tooltip).toBeVisible();
await expect(tooltip).toContainText('Tooltip with options');
});
test('should replace existing tooltip when showing new one', async ({ page }) => {
const testElements = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element1 = document.createElement('button');
element1.textContent = 'Button 1';
element1.id = 'button-1';
const element2 = document.createElement('button');
element2.textContent = 'Button 2';
element2.id = 'button-2';
container?.appendChild(element1);
container?.appendChild(element2);
return {
id1: element1.id,
id2: element2.id,
};
}, { holderId: HOLDER_ID });
// Show first tooltip
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'First tooltip');
}
}, { elementId: testElements.id1 });
let tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('First tooltip');
// Show second tooltip - should replace the first
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Second tooltip');
}
}, { elementId: testElements.id2 });
tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('Second tooltip');
await expect(tooltip).not.toContainText('First tooltip');
});
});
test.describe('hide()', () => {
test('should hide visible tooltip', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Test Button';
element.id = 'test-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
// Show tooltip
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Test tooltip');
}
}, { elementId: testElement.id });
const tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('Test tooltip');
// Hide tooltip
await page.evaluate(() => {
const editor = window.editorInstance;
if (editor?.tooltip) {
editor.tooltip.hide();
}
});
await waitForTooltipToHide(page);
});
test('should hide tooltip when hide() is called', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('div');
element.textContent = 'Test';
element.id = 'test-element';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
// Show tooltip with hiding delay
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Tooltip with delay', {
hidingDelay: 1000,
});
}
}, { elementId: testElement.id });
const tooltipBeforeHide = await waitForTooltip(page);
await expect(tooltipBeforeHide).toBeVisible();
// Hide tooltip
await page.evaluate(() => {
const editor = window.editorInstance;
if (editor?.tooltip) {
editor.tooltip.hide();
}
});
// Should hide
await waitForTooltipToHide(page);
});
test('should handle hide() when no tooltip is visible', async ({ page }) => {
// Calling hide() when no tooltip is visible should not throw
await page.evaluate(() => {
const editor = window.editorInstance;
if (editor?.tooltip) {
editor.tooltip.hide();
}
});
// Verify no tooltip appears
const tooltip = page.locator(TOOLTIP_INTERFACE_SELECTOR);
await expect(tooltip).toBeHidden();
});
});
test.describe('onHover()', () => {
test('should show tooltip on mouseenter', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Hover me';
element.id = 'hover-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.onHover(element, 'Hover tooltip');
}
}, { elementId: testElement.id });
const button = page.locator(`#${testElement.id}`);
await button.hover();
const tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('Hover tooltip');
});
test('should hide tooltip on mouseleave', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('div');
element.textContent = 'Hover area';
element.id = 'hover-area';
element.style.padding = '20px';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.onHover(element, 'Hover tooltip');
}
}, { elementId: testElement.id });
const hoverArea = page.locator(`#${testElement.id}`);
await hoverArea.hover();
const tooltip = await waitForTooltip(page);
await expect(tooltip).toBeVisible();
// Move mouse away
await page.mouse.move(0, 0);
await waitForTooltipToHide(page);
});
test('should show tooltip on hover with HTML content', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('span');
element.textContent = 'Rich tooltip';
element.id = 'rich-tooltip';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
const htmlContent = document.createElement('div');
htmlContent.innerHTML = '<em>Italic</em> content';
editor.tooltip.onHover(element, htmlContent);
}
}, { elementId: testElement.id });
const element = page.locator(`#${testElement.id}`);
await element.hover();
const tooltip = await waitForTooltip(page);
await expect(tooltip.locator('em')).toContainText('Italic');
await expect(tooltip).toContainText('content');
});
test('should show tooltip on hover with options', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Configured tooltip';
element.id = 'configured-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.onHover(element, 'Tooltip with config', {
placement: 'bottom',
delay: 200,
});
}
}, { elementId: testElement.id });
const button = page.locator(`#${testElement.id}`);
await button.hover();
// Wait for tooltip to appear (accounting for delay)
const tooltip = page.locator(TOOLTIP_INTERFACE_SELECTOR);
await expect(tooltip).toBeVisible({ timeout: 500 });
await expect(tooltip).toContainText('Tooltip with config');
});
});
test.describe('Integration', () => {
test('should handle multiple show/hide cycles', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Toggle';
element.id = 'toggle-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
// First cycle
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'First');
}
}, { elementId: testElement.id });
const firstTooltip = await waitForTooltip(page);
await expect(firstTooltip).toContainText('First');
await page.evaluate(() => {
const editor = window.editorInstance;
if (editor?.tooltip) {
editor.tooltip.hide();
}
});
await waitForTooltipToHide(page);
// Second cycle
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Second');
}
}, { elementId: testElement.id });
const tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('Second');
});
test('should work with different element types', async ({ page }) => {
const testElements = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const button = document.createElement('button');
button.textContent = 'Button';
button.id = 'test-button';
const div = document.createElement('div');
div.textContent = 'Div';
div.id = 'test-div';
const span = document.createElement('span');
span.textContent = 'Span';
span.id = 'test-span';
container?.appendChild(button);
container?.appendChild(div);
container?.appendChild(span);
return {
buttonId: button.id,
divId: div.id,
spanId: span.id,
};
}, { holderId: HOLDER_ID });
// Test with button
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Button tooltip');
}
}, { elementId: testElements.buttonId });
let tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('Button tooltip');
await page.evaluate(() => {
const editor = window.editorInstance;
if (editor?.tooltip) {
editor.tooltip.hide();
}
});
await waitForTooltipToHide(page);
// Test with div
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Div tooltip');
}
}, { elementId: testElements.divId });
tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('Div tooltip');
await page.evaluate(() => {
const editor = window.editorInstance;
if (editor?.tooltip) {
editor.tooltip.hide();
}
});
await waitForTooltipToHide(page);
// Test with span
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Span tooltip');
}
}, { elementId: testElements.spanId });
tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('Span tooltip');
});
});
test.describe('destroy()', () => {
test('should destroy tooltip library and allow reinitialization', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Test';
element.id = 'test-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
// Show tooltip before destroy
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Before destroy');
}
}, { elementId: testElement.id });
await waitForTooltip(page);
// Destroy editor (which calls tooltip.destroy())
await page.evaluate(async () => {
if (window.editorInstance) {
await window.editorInstance.destroy();
window.editorInstance = undefined;
}
});
// Recreate editor
await createEditor(page);
// Recreate test element if the holder was cleared during destroy
await page.evaluate(({ holderId, elementId }) => {
const container = document.getElementById(holderId);
let element = document.getElementById(elementId);
if (!element && container) {
element = document.createElement('button');
element.textContent = 'Test';
element.id = elementId;
container.appendChild(element);
}
}, { holderId: HOLDER_ID,
elementId: testElement.id });
// Tooltip should work after reinitialization
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'After recreate');
}
}, { elementId: testElement.id });
const tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('After recreate');
});
});
test.describe('Edge cases', () => {
test('should handle calling onHover() multiple times on same element', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Hover me';
element.id = 'hover-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
// First onHover binding
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.onHover(element, 'First binding');
}
}, { elementId: testElement.id });
// Second onHover binding (should replace first)
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.onHover(element, 'Second binding');
}
}, { elementId: testElement.id });
const button = page.locator(`#${testElement.id}`);
await button.hover();
const tooltip = await waitForTooltip(page);
// Should show the second binding
await expect(tooltip).toContainText('Second binding');
await expect(tooltip).not.toContainText('First binding');
});
test('should handle calling show() multiple times on same element', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('div');
element.textContent = 'Test';
element.id = 'test-element';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
// First show
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'First show');
}
}, { elementId: testElement.id });
let tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText('First show');
// Second show on same element
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Second show');
}
}, { elementId: testElement.id });
tooltip = await waitForTooltip(page);
// Should replace with second show
await expect(tooltip).toContainText('Second show');
await expect(tooltip).not.toContainText('First show');
});
test('should handle empty string content', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Test';
element.id = 'test-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, '');
}
}, { elementId: testElement.id });
// Tooltip should still appear (even if empty)
const tooltip = page.locator(TOOLTIP_INTERFACE_SELECTOR);
await expect(tooltip).toBeVisible();
});
test('should handle very long content', async ({ page }) => {
const longText = 'A'.repeat(500);
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('div');
element.textContent = 'Test';
element.id = 'test-element';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId, text }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, text);
}
}, { elementId: testElement.id,
text: longText });
const tooltip = await waitForTooltip(page);
await expect(tooltip).toContainText(longText.substring(0, 100));
});
test('should handle special characters in content', async ({ page }) => {
const specialChars = '<>&"\'`';
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('span');
element.textContent = 'Test';
element.id = 'test-element';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
await page.evaluate(({ elementId, text }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, text);
}
}, { elementId: testElement.id,
text: specialChars });
const tooltip = await waitForTooltip(page);
await expect(tooltip).toBeVisible();
});
test('should handle interaction between show() and onHover()', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('button');
element.textContent = 'Test';
element.id = 'test-button';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
// First bind onHover
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.onHover(element, 'Hover tooltip');
}
}, { elementId: testElement.id });
// Then show programmatically
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Programmatic tooltip');
}
}, { elementId: testElement.id });
const tooltip = await waitForTooltip(page);
// Should show programmatic tooltip
await expect(tooltip).toContainText('Programmatic tooltip');
// Hide it
await page.evaluate(() => {
const editor = window.editorInstance;
if (editor?.tooltip) {
editor.tooltip.hide();
}
});
await waitForTooltipToHide(page);
// Hover should still work
const button = page.locator(`#${testElement.id}`);
await button.hover();
const hoverTooltip = await waitForTooltip(page);
await expect(hoverTooltip).toContainText('Hover tooltip');
});
test('should handle hide() called multiple times', async ({ page }) => {
const testElement = await page.evaluate(({ holderId }) => {
const container = document.getElementById(holderId);
const element = document.createElement('div');
element.textContent = 'Test';
element.id = 'test-element';
container?.appendChild(element);
return {
id: element.id,
};
}, { holderId: HOLDER_ID });
// Show tooltip
await page.evaluate(({ elementId }) => {
const editor = window.editorInstance;
const element = document.getElementById(elementId);
if (element && editor?.tooltip) {
editor.tooltip.show(element, 'Test');
}
}, { elementId: testElement.id });
const tooltip = await waitForTooltip(page);
await expect(tooltip).toBeVisible();
// Call hide() multiple times
await page.evaluate(() => {
const editor = window.editorInstance;
if (editor?.tooltip) {
editor.tooltip.hide();
editor.tooltip.hide();
editor.tooltip.hide();
}
});
// Should still be hidden (no errors)
await waitForTooltipToHide(page);
});
});
});

View file

@ -1,7 +1,7 @@
/**
* Tooltip API
*/
import {TooltipContent, TooltipOptions} from 'codex-tooltip';
import {TooltipContent, TooltipOptions} from '../../src/components/utils/tooltip';
export interface Tooltip {
/**

1
types/index.d.ts vendored
View file

@ -138,6 +138,7 @@ declare class EditorJS {
public styles: Styles;
public toolbar: Toolbar;
public inlineToolbar: InlineToolbar;
public tooltip: Tooltip;
public readOnly: ReadOnly;
constructor(configuration?: EditorConfig|string);